From a17d0c381b91c4db749a290c344e5c8943fbc658 Mon Sep 17 00:00:00 2001 From: Alex Christensen Date: Fri, 21 Feb 2025 13:28:44 -0800 Subject: [PATCH 001/174] Use more site-isolation friendly frame tree traversal in FullscreenManager https://bugs.webkit.org/show_bug.cgi?id=288213 rdar://145304768 Reviewed by Ryosuke Niwa. In a few places we were making assumptions like: 1. The main frame is in the same process as the current document 2. A document's owner element is in the same process as the document 3. When we are traversing, if we find a document without an owner element in this process, we must be at the main frame. All 3 assumptions are invalid when site isolation is on. I've replaced the traversal with traversing the FrameTree including LocalFrames and RemoteFrames and only visiting the LocalFrames. Before this change, exiting a fullscreen third party iframe would not actually exit, as seen by the lack of UI process exitFullScreenForElement and beganExitFullScreen calls in the test output. That is fixed with this change. The style and size isn't yet correct, but those will be fixed in future PRs. I also replaced a few Deques with Vector+makeReversedRange to accomplish the same reverse iteration without the overhead of a Deque. FullscreenManager::exitFullscreen had an assumption that the exit mode is NoResize unless we know we are exiting fullscreen in the main frame. In order for exiting fullscreen to work in iframes with site isolation on, I needed to make the assumption that if the main frame is in another process we are exiting with ExitMode::Resize. This only applies with site isolation on. * LayoutTests/http/tests/site-isolation/fullscreen-expected.txt: * LayoutTests/http/tests/site-isolation/fullscreen.html: * Source/WebCore/dom/FullscreenManager.cpp: (WebCore::documentsToUnfullscreen): (WebCore::FullscreenManager::exitFullscreen): (WebCore::FullscreenManager::finishExitFullscreen): (WebCore::FullscreenManager::willEnterFullscreen): (WebCore::FullscreenManager::didExitFullscreen): * Source/WebCore/dom/FullscreenManager.h: Canonical link: https://commits.webkit.org/290822@main --- .../site-isolation/fullscreen-expected.txt | 8 +- .../http/tests/site-isolation/fullscreen.html | 5 +- ...t-fullscreen-nested-in-iframe-expected.txt | 2 +- Source/WebCore/dom/FullscreenManager.cpp | 78 ++++++++++--------- Source/WebCore/dom/FullscreenManager.h | 2 +- 5 files changed, 52 insertions(+), 43 deletions(-) diff --git a/LayoutTests/http/tests/site-isolation/fullscreen-expected.txt b/LayoutTests/http/tests/site-isolation/fullscreen-expected.txt index 879bd9e58554a..70cf5b7cf5b6b 100644 --- a/LayoutTests/http/tests/site-isolation/fullscreen-expected.txt +++ b/LayoutTests/http/tests/site-isolation/fullscreen-expected.txt @@ -1,11 +1,17 @@ supportsFullScreen() == true enterFullScreenForElement() beganEnterFullScreen() - initialRect.size: {300, 150}, finalRect.size: {300, 150} +exitFullScreenForElement() +beganExitFullScreen() - initialRect.size: {300, 150}, finalRect.size: {300, 150} FIXME: Size after entering should be 600x800 like it is with site isolation off. The size currently comes from screenRectOfContents. -Also, there should be exitFullScreenForElement and beganExitFullScreen callbacks like there are with site isolation off. +Also, the iframe's border 'inset' style should be 'none' after entering fullscreen. Size after entering fullscreen: 150x300 +iframe border style after transition: 0px inset rgb(0, 0, 0) + Size after exiting fullscreen: 150x300 +iframe border style after transition: 0px inset rgb(0, 0, 0) + diff --git a/LayoutTests/http/tests/site-isolation/fullscreen.html b/LayoutTests/http/tests/site-isolation/fullscreen.html index f01238da10f03..4e2a9d6beba75 100644 --- a/LayoutTests/http/tests/site-isolation/fullscreen.html +++ b/LayoutTests/http/tests/site-isolation/fullscreen.html @@ -7,12 +7,13 @@ } addEventListener("message", (event) => { document.getElementById("mylog").innerHTML += event.data + "
"; + document.getElementById("mylog").innerHTML += 'iframe border style after transition: ' + getComputedStyle(document.getElementById("frame")).getPropertyValue('border') + "

"; if (event.data.startsWith('Size after exiting') && window.testRunner) { testRunner.notifyDone() } }); - +
FIXME: Size after entering should be 600x800 like it is with site isolation off.
The size currently comes from screenRectOfContents.
-Also, there should be exitFullScreenForElement and beganExitFullScreen callbacks like there are with site isolation off.

+Also, the iframe's border 'inset' style should be 'none' after entering fullscreen.

diff --git a/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/document-exit-fullscreen-nested-in-iframe-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/document-exit-fullscreen-nested-in-iframe-expected.txt index 6069b834e7b8f..52dfe7ac986f6 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/document-exit-fullscreen-nested-in-iframe-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/document-exit-fullscreen-nested-in-iframe-expected.txt @@ -1,4 +1,4 @@ -FAIL Exit fullscreen for nested fullscreen inside an iframe assert_equals: expected null but got Element node "_s } }, + { "/frame"_s, { ""_s } } + }, HTTPServer::Protocol::HttpsProxy); + auto [webView1, navigationDelegate] = siteIsolatedViewAndDelegate(server); + [webView1 loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://example.com/example"]]]; + EXPECT_WK_STREQ([webView1 _test_waitForAlert], "done"); + + auto [webView2, navigationDelegate2] = siteIsolatedViewAndDelegate(server); + [webView2 _restoreSessionState:[webView1 _sessionState] andNavigate:YES]; + EXPECT_WK_STREQ([webView2 _test_waitForAlert], "done"); +} + static void testNavigateIframeBackForward(NSString *navigationURL, bool restoreSessionState) { HTTPServer server({ From a1c69c005cb386bb3efbf695ae77b0556ce4b78e Mon Sep 17 00:00:00 2001 From: Robert Jenner Date: Fri, 21 Feb 2025 15:36:40 -0800 Subject: [PATCH 012/174] Unreviewed, reverting 290825@main (67d7e1c8fe78) https://bugs.webkit.org/show_bug.cgi?id=288233 rdar://145324925 Broke the build. Reverted change: Refine the construction of WKWebExtension in Swift. https://bugs.webkit.org/show_bug.cgi?id=288129 rdar://145235810 290825@main (67d7e1c8fe78) Canonical link: https://commits.webkit.org/290833@main --- .../UIProcess/API/Cocoa/WKWebExtension.h | 4 +- .../UIProcess/API/Cocoa/WKWebExtension.mm | 94 ++++++------------- .../API/Cocoa/WKWebExtensionPrivate.h | 21 ++--- .../API/Cocoa/WebKitSwiftOverlay.swift | 25 ----- 4 files changed, 39 insertions(+), 105 deletions(-) diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.h b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.h index 2764447a5433d..ec09fbf7509ce 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.h +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.h @@ -82,7 +82,7 @@ WK_CLASS_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA), visionos(WK_XROS_TBA)) WK @discussion The app extension bundle must contain a `manifest.json` file in its resources directory. If the manifest is invalid or missing, or the bundle is otherwise improperly configured, an error will be returned. */ -+ (void)extensionWithAppExtensionBundle:(NSBundle *)appExtensionBundle completionHandler:(void (^)(WKWebExtension * _Nullable extension, NSError * _Nullable error))completionHandler NS_REFINED_FOR_SWIFT; ++ (void)extensionWithAppExtensionBundle:(NSBundle *)appExtensionBundle completionHandler:(void (^)(WKWebExtension * WK_NULLABLE_RESULT extension, NSError * _Nullable error))completionHandler WK_SWIFT_ASYNC_THROWS_ON_FALSE(1); /*! @abstract Returns a web extension initialized with a specified resource base URL, which can point to either a directory or a ZIP archive. @@ -91,7 +91,7 @@ WK_CLASS_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA), visionos(WK_XROS_TBA)) WK @discussion The URL must be a file URL that points to either a directory with a `manifest.json` file or a ZIP archive containing a `manifest.json` file. If the manifest is invalid or missing, or the URL points to an unsupported format or invalid archive, an error will be returned. */ -+ (void)extensionWithResourceBaseURL:(NSURL *)resourceBaseURL completionHandler:(void (^)(WKWebExtension * _Nullable extension, NSError * _Nullable error))completionHandler NS_REFINED_FOR_SWIFT; ++ (void)extensionWithResourceBaseURL:(NSURL *)resourceBaseURL completionHandler:(void (^)(WKWebExtension * WK_NULLABLE_RESULT extension, NSError * _Nullable error))completionHandler WK_SWIFT_ASYNC_THROWS_ON_FALSE(1); /*! @abstract An array of all errors that occurred during the processing of the extension. diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.mm b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.mm index 279bd260386f6..d3c153560a6f3 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.mm @@ -86,47 +86,29 @@ + (void)extensionWithResourceBaseURL:(NSURL *)resourceBaseURL completionHandler: }); } -- (instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error -{ - return [self initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:nil error:error]; -} - -- (instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error -{ - return [self initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; -} - -- (instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle resourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error -{ - return [self initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:resourceBaseURL error:error]; -} - -- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest -{ - return [self initWithManifestDictionary:manifest resources:nil]; -} - -- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources +// FIXME: Remove after Safari has adopted new methods. +- (instancetype)initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error { - return [self initWithManifestDictionary:manifest resources:resources]; + return [self _initWithAppExtensionBundle:appExtensionBundle error:error]; } -- (instancetype)_initWithResources:(NSDictionary *)resources +- (instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error { - return [self initWithResources:resources]; + return [self _initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:nil error:error]; } -- (instancetype)initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error +// FIXME: Remove after Safari has adopted new methods. +- (instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error { - return [self initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:nil error:error]; + return [self _initWithResourceBaseURL:resourceBaseURL error:error]; } -- (instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error +- (instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error { - return [self initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; + return [self _initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; } -- (instancetype)initWithAppExtensionBundle:(nullable NSBundle *)appExtensionBundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error +- (instancetype)_initWithAppExtensionBundle:(nullable NSBundle *)appExtensionBundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error { NSParameterAssert(appExtensionBundle || resourceBaseURL); @@ -156,14 +138,14 @@ - (instancetype)initWithAppExtensionBundle:(nullable NSBundle *)appExtensionBund return self; } -- (instancetype)initWithManifestDictionary:(NSDictionary *)manifest +- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest { NSParameterAssert([manifest isKindOfClass:NSDictionary.class]); - return [self initWithManifestDictionary:manifest resources:nil]; + return [self _initWithManifestDictionary:manifest resources:nil]; } -- (instancetype)initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources +- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources { NSParameterAssert([manifest isKindOfClass:NSDictionary.class]); @@ -175,7 +157,7 @@ - (instancetype)initWithManifestDictionary:(NSDictionary *)manif return self; } -- (instancetype)initWithResources:(NSDictionary *)resources +- (instancetype)_initWithResources:(NSDictionary *)resources { NSParameterAssert([resources isKindOfClass:NSDictionary.class]); @@ -367,64 +349,44 @@ + (void)extensionWithResourceBaseURL:(NSURL *)resourceBaseURL completionHandler: completionHandler(nil, [NSError errorWithDomain:NSCocoaErrorDomain code:NSFeatureUnsupportedError userInfo:nil]); } -- (instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error -{ - return [self initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:nil error:error]; -} - -- (instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error -{ - return [self initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; -} - -- (instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle resourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error -{ - return [self initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:resourceBaseURL error:error]; -} - -- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest -{ - return [self initWithManifestDictionary:manifest resources:nil]; -} - -- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources +- (instancetype)initWithAppExtensionBundle:(NSBundle *)bundle error:(NSError **)error { - return [self initWithManifestDictionary:manifest resources:resources]; + return [self _initWithAppExtensionBundle:bundle error:error]; } -- (instancetype)_initWithResources:(NSDictionary *)resources +- (instancetype)_initWithAppExtensionBundle:(NSBundle *)bundle error:(NSError **)error { - return [self initWithResources:resources]; + return [self _initWithAppExtensionBundle:bundle resourceBaseURL:nil error:error]; } -- (instancetype)initWithAppExtensionBundle:(NSBundle *)bundle error:(NSError **)error +- (instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error { - return [self initWithAppExtensionBundle:bundle resourceBaseURL:nil error:error]; + return [self _initWithResourceBaseURL:resourceBaseURL error:error]; } -- (instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error +- (instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error { - return [self initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; + return [self _initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; } -- (instancetype)initWithAppExtensionBundle:(nullable NSBundle *)bundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error +- (instancetype)_initWithAppExtensionBundle:(nullable NSBundle *)bundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error { if (error) *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFeatureUnsupportedError userInfo:nil]; return nil; } -- (instancetype)initWithManifestDictionary:(NSDictionary *)manifest +- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest { - return [self initWithManifestDictionary:manifest resources:nil]; + return [self _initWithManifestDictionary:manifest resources:nil]; } -- (instancetype)initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources +- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources { return nil; } -- (instancetype)initWithResources:(NSDictionary *)resources +- (instancetype)_initWithResources:(NSDictionary *)resources { return nil; } diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtensionPrivate.h b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtensionPrivate.h index 6175d61ccf4fa..2e524adcc0fdb 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtensionPrivate.h +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtensionPrivate.h @@ -29,12 +29,9 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @interface WKWebExtension () -- (nullable instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error NS_SWIFT_UNAVAILABLE("Use init(appExtensionBundle:)."); -- (nullable instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error NS_SWIFT_UNAVAILABLE("Use init(resourceBaseURL:)."); -- (nullable instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle resourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error NS_SWIFT_UNAVAILABLE("Use init(appExtensionBundle:resourceBaseURL:)."); -- (nullable instancetype)_initWithManifestDictionary:(NSDictionary *)manifest NS_SWIFT_UNAVAILABLE("Use init(manifestDictionary:)."); -- (nullable instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(nullable NSDictionary *)resources NS_SWIFT_UNAVAILABLE("Use init(manifestDictionary:resources:)."); -- (nullable instancetype)_initWithResources:(NSDictionary *)resources NS_SWIFT_UNAVAILABLE("Use init(resources:)."); +// FIXME: Remove after Safari has adopted new methods. +- (nullable instancetype)initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error; +- (nullable instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error; /*! @abstract Returns a web extension initialized with a specified app extension bundle. @@ -42,7 +39,7 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @param error Set to \c nil or an error instance if an error occurred. @result An initialized web extension, or `nil` if the object could not be initialized due to an error. */ -- (nullable instancetype)initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error NS_REFINED_FOR_SWIFT; +- (nullable instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error; /*! @abstract Returns a web extension initialized with a specified resource base URL. @@ -51,7 +48,7 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @result An initialized web extension, or `nil` if the object could not be initialized due to an error. @discussion The URL must be a file URL that points to either a directory containing a `manifest.json` file or a valid ZIP archive. */ -- (nullable instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error NS_REFINED_FOR_SWIFT; +- (nullable instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error; /*! @abstract Returns a web extension initialized with a specified app extension bundle and resource base URL. @@ -62,14 +59,14 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @discussion Either the app extension bundle or the resource base URL (which can point to a directory or a valid ZIP archive) must be provided. This initializer is useful when the extension resources are in a different location from the app extension bundle used for native messaging. */ -- (nullable instancetype)initWithAppExtensionBundle:(nullable NSBundle *)appExtensionBundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)_initWithAppExtensionBundle:(nullable NSBundle *)appExtensionBundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error NS_DESIGNATED_INITIALIZER; /*! @abstract Returns a web extension initialized with a specified manifest dictionary. @param manifest The dictionary containing the manifest data for the web extension. @result An initialized web extension, or `nil` if the object could not be initialized due to an error. */ -- (nullable instancetype)initWithManifestDictionary:(NSDictionary *)manifest; +- (nullable instancetype)_initWithManifestDictionary:(NSDictionary *)manifest; /*! @abstract Returns a web extension initialized with a specified manifest dictionary and resources. @@ -79,7 +76,7 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @discussion The resources dictionary provides additional data required for the web extension. Paths in resources can have subdirectories, such as `_locales/en/messages.json`. */ -- (nullable instancetype)initWithManifestDictionary:(NSDictionary *)manifest resources:(nullable NSDictionary *)resources NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(nullable NSDictionary *)resources NS_DESIGNATED_INITIALIZER; /*! @abstract Returns a web extension initialized with specified resources. @@ -88,7 +85,7 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @discussion The resources dictionary must provide at least the `manifest.json` resource. Paths in resources can have subdirectories, such as `_locales/en/messages.json`. */ -- (nullable instancetype)initWithResources:(NSDictionary *)resources NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)_initWithResources:(NSDictionary *)resources NS_DESIGNATED_INITIALIZER; /*! @abstract A Boolean value indicating whether the extension background content is a service worker. */ @property (readonly, nonatomic) BOOL _hasServiceWorkerBackgroundContent; diff --git a/Source/WebKit/UIProcess/API/Cocoa/WebKitSwiftOverlay.swift b/Source/WebKit/UIProcess/API/Cocoa/WebKitSwiftOverlay.swift index 80d29f01bae64..994071d3a2df9 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WebKitSwiftOverlay.swift +++ b/Source/WebKit/UIProcess/API/Cocoa/WebKitSwiftOverlay.swift @@ -25,10 +25,6 @@ #if !os(tvOS) && !os(watchOS) -#if compiler(>=6.0) -internal import WebKit_Private -#endif - #if USE_APPLE_INTERNAL_SDK @_spi(CTypeConversion) import Network #endif @@ -89,25 +85,7 @@ extension WKWebView { } #endif -#if compiler(>=6.0) @available(iOS 18.4, macOS 15.4, visionOS 2.4, *) -@available(watchOS, unavailable) -@available(tvOS, unavailable) -extension WKWebExtension { - public convenience init(appExtensionBundle: Bundle) async throws { - // FIXME: Make the WebExtension class load data on a background thread. - try self.init(appExtensionBundle: appExtensionBundle, resourceBaseURL: nil) - } - - public convenience init(resourceBaseURL: URL) async throws { - // FIXME: Make the WebExtension class load data on a background thread. - try self.init(appExtensionBundle: nil, resourceBaseURL: resourceBaseURL) - } -} - -@available(iOS 18.4, macOS 15.4, visionOS 2.4, *) -@available(watchOS, unavailable) -@available(tvOS, unavailable) extension WKWebExtensionController { public func didCloseTab(_ closedTab: WKWebExtensionTab, windowIsClosing: Bool = false) { __didClose(closedTab, windowIsClosing: windowIsClosing) @@ -123,8 +101,6 @@ extension WKWebExtensionController { } @available(iOS 18.4, macOS 15.4, visionOS 2.4, *) -@available(watchOS, unavailable) -@available(tvOS, unavailable) extension WKWebExtensionContext { public func didCloseTab(_ closedTab: WKWebExtensionTab, windowIsClosing: Bool = false) { __didClose(closedTab, windowIsClosing: windowIsClosing) @@ -138,7 +114,6 @@ extension WKWebExtensionContext { __didMove(movedTab, from: index, in: oldWindow) } } -#endif // FIXME: Need to declare ProxyConfiguration SPI in order to build and test // this with public SDKs (https://bugs.webkit.org/show_bug.cgi?id=280911). From d2e94b018a5a96215b37f681db435b0f99b5e9a1 Mon Sep 17 00:00:00 2001 From: Per Arne Vollan Date: Fri, 21 Feb 2025 15:43:37 -0800 Subject: [PATCH 013/174] Fix memory leak in sandbox extension code https://bugs.webkit.org/show_bug.cgi?id=288172 rdar://145238621 Reviewed by Chris Dumez. The function SandboxExtensionImpl::sandboxExtensionForType currently returns a C string that needs to be deallocated by the caller. Not all callers of this function are deallocating the memory. This patch addresses this issue by changing the function to return a CString. The caller does no longer have to deallocate the C string returned from the sandbox functions that issued the extension. The deallocation of the C string is now done inside the method itself. * Source/WebKit/Shared/Cocoa/SandboxExtensionCocoa.mm: (WebKit::SandboxExtensionImpl::sandboxExtensionForType): * Source/WebKit/Shared/SandboxExtension.h: Canonical link: https://commits.webkit.org/290834@main --- .../Shared/Cocoa/SandboxExtensionCocoa.mm | 62 ++++++++++--------- Source/WebKit/Shared/SandboxExtension.h | 2 +- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/Source/WebKit/Shared/Cocoa/SandboxExtensionCocoa.mm b/Source/WebKit/Shared/Cocoa/SandboxExtensionCocoa.mm index a5a530cca83f2..3f4cd0f89c4a2 100644 --- a/Source/WebKit/Shared/Cocoa/SandboxExtensionCocoa.mm +++ b/Source/WebKit/Shared/Cocoa/SandboxExtensionCocoa.mm @@ -81,37 +81,41 @@ return byteCast(m_token.span()); } -char* SandboxExtensionImpl::sandboxExtensionForType(const char* path, SandboxExtension::Type type, std::optional auditToken, OptionSet flags) -{ - uint32_t extensionFlags = 0; - if (flags & SandboxExtension::Flags::NoReport) - extensionFlags |= SANDBOX_EXTENSION_NO_REPORT; - if (flags & SandboxExtension::Flags::DoNotCanonicalize) - extensionFlags |= SANDBOX_EXTENSION_CANONICAL; - - switch (type) { - case SandboxExtension::Type::ReadOnly: - return sandbox_extension_issue_file(APP_SANDBOX_READ, path, extensionFlags); - case SandboxExtension::Type::ReadWrite: - return sandbox_extension_issue_file(APP_SANDBOX_READ_WRITE, path, extensionFlags); - case SandboxExtension::Type::Mach: - if (!auditToken) - return sandbox_extension_issue_mach("com.apple.webkit.extension.mach", path, extensionFlags); - return sandbox_extension_issue_mach_to_process("com.apple.webkit.extension.mach", path, extensionFlags, *auditToken); - case SandboxExtension::Type::IOKit: - if (!auditToken) - return sandbox_extension_issue_iokit_registry_entry_class("com.apple.webkit.extension.iokit", path, extensionFlags); - return sandbox_extension_issue_iokit_registry_entry_class_to_process("com.apple.webkit.extension.iokit", path, extensionFlags, *auditToken); - case SandboxExtension::Type::Generic: - return sandbox_extension_issue_generic(path, extensionFlags); - case SandboxExtension::Type::ReadByProcess: - if (!auditToken) - return nullptr; +CString SandboxExtensionImpl::sandboxExtensionForType(const char* path, SandboxExtension::Type type, std::optional auditToken, OptionSet flags) +{ + auto sandboxExtension = [&] { + uint32_t extensionFlags = 0; + if (flags & SandboxExtension::Flags::NoReport) + extensionFlags |= SANDBOX_EXTENSION_NO_REPORT; + if (flags & SandboxExtension::Flags::DoNotCanonicalize) + extensionFlags |= SANDBOX_EXTENSION_CANONICAL; + + switch (type) { + case SandboxExtension::Type::ReadOnly: + return std::unique_ptr(sandbox_extension_issue_file(APP_SANDBOX_READ, path, extensionFlags), free); + case SandboxExtension::Type::ReadWrite: + return std::unique_ptr(sandbox_extension_issue_file(APP_SANDBOX_READ_WRITE, path, extensionFlags), free); + case SandboxExtension::Type::Mach: + if (!auditToken) + return std::unique_ptr(sandbox_extension_issue_mach("com.apple.webkit.extension.mach", path, extensionFlags), free); + return std::unique_ptr(sandbox_extension_issue_mach_to_process("com.apple.webkit.extension.mach", path, extensionFlags, *auditToken), free); + case SandboxExtension::Type::IOKit: + if (!auditToken) + return std::unique_ptr(sandbox_extension_issue_iokit_registry_entry_class("com.apple.webkit.extension.iokit", path, extensionFlags), free); + return std::unique_ptr(sandbox_extension_issue_iokit_registry_entry_class_to_process("com.apple.webkit.extension.iokit", path, extensionFlags, *auditToken), free); + case SandboxExtension::Type::Generic: + return std::unique_ptr(sandbox_extension_issue_generic(path, extensionFlags), free); + case SandboxExtension::Type::ReadByProcess: + if (!auditToken) + return std::unique_ptr(nullptr, free); #if PLATFORM(MAC) - extensionFlags |= SANDBOX_EXTENSION_USER_INTENT; + extensionFlags |= SANDBOX_EXTENSION_USER_INTENT; #endif - return sandbox_extension_issue_file_to_process(APP_SANDBOX_READ, path, extensionFlags, *auditToken); - } + return std::unique_ptr(sandbox_extension_issue_file_to_process(APP_SANDBOX_READ, path, extensionFlags, *auditToken), free); + } + }(); + + return CString(sandboxExtension.get()); } SandboxExtensionImpl::SandboxExtensionImpl(const char* path, SandboxExtension::Type type, std::optional auditToken, OptionSet flags) diff --git a/Source/WebKit/Shared/SandboxExtension.h b/Source/WebKit/Shared/SandboxExtension.h index ad5ff9c23d197..9bacf72389734 100644 --- a/Source/WebKit/Shared/SandboxExtension.h +++ b/Source/WebKit/Shared/SandboxExtension.h @@ -69,7 +69,7 @@ class SandboxExtensionImpl { : m_token(std::exchange(other.m_token, CString())) , m_handle(std::exchange(other.m_handle, 0)) { } private: - char* sandboxExtensionForType(const char* path, SandboxExtensionType, std::optional, OptionSet); + CString sandboxExtensionForType(const char* path, SandboxExtensionType, std::optional, OptionSet); SandboxExtensionImpl(const char* path, SandboxExtensionType, std::optional, OptionSet); From 028359956b37e34c713794e96580dcdd52b7c83a Mon Sep 17 00:00:00 2001 From: Fujii Hironori Date: Fri, 21 Feb 2025 16:05:42 -0800 Subject: [PATCH 014/174] [Win] Unreviewed test gardening * LayoutTests/platform/win/TestExpectations: Canonical link: https://commits.webkit.org/290835@main --- LayoutTests/platform/win/TestExpectations | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/LayoutTests/platform/win/TestExpectations b/LayoutTests/platform/win/TestExpectations index 42a629ff394b9..28a2dec06089a 100644 --- a/LayoutTests/platform/win/TestExpectations +++ b/LayoutTests/platform/win/TestExpectations @@ -1082,6 +1082,10 @@ imported/w3c/web-platform-tests/scroll-animations/scroll-timelines/layout-change imported/w3c/web-platform-tests/scroll-animations/scroll-timelines/animation-with-animatable-interface.html [ ImageOnlyFailure ] imported/w3c/web-platform-tests/scroll-animations/scroll-timelines/animation-with-display-none.html [ ImageOnlyFailure ] +imported/w3c/web-platform-tests/scroll-animations/scroll-timelines/animation-with-transform.html [ ImageOnlyFailure ] +imported/w3c/web-platform-tests/scroll-animations/scroll-timelines/two-animations-attach-to-same-scroll-timeline-cancel-one.html [ ImageOnlyFailure ] +imported/w3c/web-platform-tests/scroll-animations/scroll-timelines/two-animations-attach-to-same-scroll-timeline.html [ ImageOnlyFailure ] + webkit.org/b/263870 imported/w3c/web-platform-tests/scroll-animations/css/animation-range-ignored.html [ Skip ] webkit.org/b/263870 imported/w3c/web-platform-tests/scroll-animations/scroll-timelines/scroll-timeline-range.html [ Skip ] From 32a639f3ab677e32649dd4a33fc80c313feb353e Mon Sep 17 00:00:00 2001 From: Nitin Mahendru Date: Fri, 21 Feb 2025 16:12:08 -0800 Subject: [PATCH 015/174] Fix xcode file landed in 290795_main rdar://145328801 https://bugs.webkit.org/show_bug.cgi?id=288255 Reviewed by Geoffrey Garen. These edits happen as soon as I open the project in xcode. So I am just committing them. I talked to Gerald(author of 290795@main) and they mentioned that they had manually edited the file and maybe that edit missed a sorting rule that xcode likes. * Source/WebCore/WebCore.xcodeproj/project.pbxproj: Canonical link: https://commits.webkit.org/290836@main --- Source/WebCore/WebCore.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj index 48c1a4a75df74..825269af0e9b4 100644 --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj @@ -2851,8 +2851,8 @@ 7261481C2A97C66F00B75E32 /* DisplayListItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 7261481B2A97BD9B00B75E32 /* DisplayListItem.h */; settings = {ATTRIBUTES = (Private, ); }; }; 7263D505292C472D00CA9D10 /* GraphicsStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 7263D503292C472B00CA9D10 /* GraphicsStyle.h */; settings = {ATTRIBUTES = (Private, ); }; }; 726516E62C9CB75600647895 /* ContentsFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 726516E42C9BB70E00647895 /* ContentsFormat.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 726516F72CA4B4A300647895 /* ContentsFormatCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 726516F52CA4AEDC00647895 /* ContentsFormatCocoa.h */; settings = {ATTRIBUTES = (Private, ); }; }; 726516E62C9CB75600647896 /* PlatformDynamicRangeLimit.h in Headers */ = {isa = PBXBuildFile; fileRef = 726516E42C9BB70E00647896 /* PlatformDynamicRangeLimit.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 726516F72CA4B4A300647895 /* ContentsFormatCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 726516F52CA4AEDC00647895 /* ContentsFormatCocoa.h */; settings = {ATTRIBUTES = (Private, ); }; }; 726CDE202759735000A445B2 /* FilterEffectGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = 729661A2275960A500E7DF9B /* FilterEffectGeometry.h */; settings = {ATTRIBUTES = (Private, ); }; }; 726D56E2253AE28D0002EF90 /* PlatformImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 726D56E1253AE0430002EF90 /* PlatformImage.h */; settings = {ATTRIBUTES = (Private, ); }; }; 726D56E3253AE3660002EF90 /* NativeImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 55A336F61D8209F40022C4C7 /* NativeImage.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -13409,10 +13409,10 @@ 7263D503292C472B00CA9D10 /* GraphicsStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GraphicsStyle.h; sourceTree = ""; }; 7263D504292C472C00CA9D10 /* GraphicsStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GraphicsStyle.cpp; sourceTree = ""; }; 726516E42C9BB70E00647895 /* ContentsFormat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContentsFormat.h; sourceTree = ""; }; - 726516E52C9CB59900647895 /* ContentsFormat.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContentsFormat.cpp; sourceTree = ""; }; - 726516F52CA4AEDC00647895 /* ContentsFormatCocoa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContentsFormatCocoa.h; sourceTree = ""; }; 726516E42C9BB70E00647896 /* PlatformDynamicRangeLimit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlatformDynamicRangeLimit.h; sourceTree = ""; }; + 726516E52C9CB59900647895 /* ContentsFormat.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContentsFormat.cpp; sourceTree = ""; }; 726516E52C9CB59900647896 /* PlatformDynamicRangeLimit.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformDynamicRangeLimit.cpp; sourceTree = ""; }; + 726516F52CA4AEDC00647895 /* ContentsFormatCocoa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContentsFormatCocoa.h; sourceTree = ""; }; 7266F0132241BCE200833975 /* SVGPropertyAnimatorFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SVGPropertyAnimatorFactory.h; sourceTree = ""; }; 7266F0142241BFB200833975 /* SVGPrimitivePropertyAnimatorImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SVGPrimitivePropertyAnimatorImpl.h; sourceTree = ""; }; 7266F0152241C09800833975 /* SVGPrimitivePropertyAnimator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SVGPrimitivePropertyAnimator.h; sourceTree = ""; }; From d1ed61f568c7dbcfca9b7f7f535618d03b6fa820 Mon Sep 17 00:00:00 2001 From: Ryan Fuller Date: Fri, 21 Feb 2025 16:14:51 -0800 Subject: [PATCH 016/174] Configure now playing media session to support external playback https://bugs.webkit.org/show_bug.cgi?id=287684 rdar://137156850 Reviewed by Andy Estes. Add a new visionOS WKWebView method to enable configuring the now playing media session for external playback. This puts the visionOS media player into a custom presentation (external) and returns the media player's view controller to the caller who has the responsibility to present it. It also provides a method to exit this external playback. On entering presentation, it sets the playerIdentifier for the video element which normally gets set as part of the fullscreen flow but we are bypassing that route in this mode -- without this the media player will not be able to render. It also puts LinearMediaPlayer in a new presentationState (.external) which allows WebKit to configure the player as needed without affecting video playing outside of this feature. Finally, it makes and returns the player view controller. On exiting presentation, it sets the state of linearMediaPlayer back to .inline presentation and tears down the configuration. Importantly, it clears the video receiver endpoint to allow the media session to render in its non-LMK presentation. While this mode is similar conceptually to fullscreen, it intentionally does not latch onto the fullscreen element state, as it should be a separate mode and the media element should not falsely believe it is in a fullscreen presentation. While in external presentation, fullscreen mode is prevented from being entered. Note: Remove LinearMediaPlayer's fullscreenSceneBehavior property until it is able to be published (rdar://122435030). * Source/WebCore/html/HTMLMediaElement.cpp: (WebCore::HTMLMediaElement::setPlayerIdentifierForVideoElement): * Source/WebCore/html/HTMLMediaElement.h: * Source/WebCore/page/ChromeClient.h: (WebCore::ChromeClient::setPlayerIdentifierForVideoElement): * Source/WebCore/platform/cocoa/PlaybackSessionModel.h: * Source/WebCore/platform/cocoa/PlaybackSessionModelMediaElement.h: * Source/WebCore/platform/cocoa/PlaybackSessionModelMediaElement.mm: (WebCore::PlaybackSessionModelMediaElement::setPlayerIdentifierForVideoElement): * Source/WebCore/platform/ios/PlaybackSessionInterfaceIOS.h: * Source/WebCore/platform/ios/PlaybackSessionInterfaceIOS.mm: (WebCore::PlaybackSessionInterfaceIOS::setVideoPresentationInterface): * Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.h: * Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm: (WebCore::VideoPresentationInterfaceIOS::VideoPresentationInterfaceIOS): (WebCore::VideoPresentationInterfaceIOS::enterExternalPlayback): (WebCore::VideoPresentationInterfaceIOS::exitExternalPlayback): * Source/WebCore/platform/ios/WebVideoFullscreenControllerAVKit.mm: * Source/WebKit/Platform/ios/PlaybackSessionInterfaceLMK.mm: (-[WKLinearMediaPlayerDelegate linearMediaPlayerClearVideoReceiverEndpoint:]): (WebKit::PlaybackSessionInterfaceLMK::supportsLinearMediaPlayerChanged): * Source/WebKit/Platform/ios/VideoPresentationInterfaceLMK.h: * Source/WebKit/Platform/ios/VideoPresentationInterfaceLMK.mm: (WebKit::VideoPresentationInterfaceLMK::enterExternalPlayback): (WebKit::VideoPresentationInterfaceLMK::exitExternalPlayback): * Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm: (-[WKWebView _enterExternalPlaybackForNowPlayingMediaSessionWithCompletionHandler:]): (-[WKWebView _exitExternalPlaybackWithCompletionHandler:]): * Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h: * Source/WebKit/UIProcess/Cocoa/PlaybackSessionManagerProxy.h: * Source/WebKit/UIProcess/Cocoa/PlaybackSessionManagerProxy.mm: (WebKit::PlaybackSessionModelContext::setPlayerIdentifierForVideoElement): (WebKit::PlaybackSessionManagerProxy::setPlayerIdentifierForVideoElement): * Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm: (WebKit::WebPageProxy::enterExternalPlaybackForNowPlayingMediaSession): (WebKit::WebPageProxy::exitExternalPlayback): * Source/WebKit/UIProcess/WebPageProxy.cpp: (WebKit::WebPageProxy::setPlayerIdentifierForVideoElement): * Source/WebKit/UIProcess/WebPageProxy.h: * Source/WebKit/WebKitSwift/LinearMediaKit/LinearMediaPlayer.swift: (SwiftOnlyData.fullscreenSceneBehaviors): (WKSLinearMediaPlayer.enterExternalPresentation(_:(any Error)?) -> Void:)): (WKSLinearMediaPlayer.exitExternalPresentation(_:(any Error)?) -> Void:)): (WKSLinearMediaPlayer.enterFullscreen(_:(any Error)?) -> Void:)): (WKSLinearMediaPlayer.exitFullscreen(_:(any Error)?) -> Void:)): (WKSLinearMediaPlayer.presentationStateChanged(_:)): (WKSLinearMediaPlayer.fullscreenSceneBehaviorsPublisher): (WKSLinearMediaPlayer.toggleInlineMode): (WKSLinearMediaPlayer.willEnterFullscreen): (WKSLinearMediaPlayer.willExitFullscreen): * Source/WebKit/WebKitSwift/LinearMediaKit/LinearMediaTypes.swift: (WKSLinearMediaPresentationState.description): * Source/WebKit/WebKitSwift/LinearMediaKit/WKSLinearMediaPlayer.h: * Source/WebKit/WebKitSwift/LinearMediaKit/WKSLinearMediaTypes.h: * Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp: (WebKit::WebChromeClient::setPlayerIdentifierForVideoElement): * Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h: * Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.h: * Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.messages.in: * Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.mm: (WebKit::PlaybackSessionManager::setPlayerIdentifierForVideoElement): * Source/WebKit/WebProcess/cocoa/VideoPresentationManager.h: * Source/WebKit/WebProcess/cocoa/VideoPresentationManager.mm: (WebKit::VideoPresentationManager::setPlayerIdentifierForVideoElement): Canonical link: https://commits.webkit.org/290837@main --- Source/WebCore/html/HTMLMediaElement.cpp | 18 +++++++ Source/WebCore/html/HTMLMediaElement.h | 1 + Source/WebCore/page/ChromeClient.h | 1 + .../platform/cocoa/PlaybackSessionModel.h | 1 + .../cocoa/PlaybackSessionModelMediaElement.h | 1 + .../cocoa/PlaybackSessionModelMediaElement.mm | 10 ++++ .../ios/PlaybackSessionInterfaceIOS.h | 4 ++ .../ios/PlaybackSessionInterfaceIOS.mm | 5 ++ .../ios/VideoPresentationInterfaceIOS.h | 5 ++ .../ios/VideoPresentationInterfaceIOS.mm | 17 +++++++ .../ios/WebVideoFullscreenControllerAVKit.mm | 1 + .../ios/PlaybackSessionInterfaceLMK.mm | 13 +++++ .../ios/VideoPresentationInterfaceLMK.h | 3 ++ .../ios/VideoPresentationInterfaceLMK.mm | 51 +++++++++++++++++++ .../UIProcess/API/Cocoa/WKWebViewPrivate.h | 3 ++ .../WebKit/UIProcess/API/ios/WKWebViewIOS.mm | 29 +++++++++++ .../Cocoa/PlaybackSessionManagerProxy.h | 2 + .../Cocoa/PlaybackSessionManagerProxy.mm | 13 +++++ .../UIProcess/Cocoa/WebPageProxyCocoa.mm | 36 ++++++++++++- Source/WebKit/UIProcess/WebPageProxy.cpp | 17 +++++++ Source/WebKit/UIProcess/WebPageProxy.h | 7 +++ .../LinearMediaKit/LinearMediaPlayer.swift | 49 +++++++++++++++--- .../LinearMediaKit/LinearMediaTypes.swift | 2 + .../LinearMediaKit/WKSLinearMediaPlayer.h | 4 +- .../LinearMediaKit/WKSLinearMediaTypes.h | 3 +- .../WebCoreSupport/WebChromeClient.cpp | 5 ++ .../WebCoreSupport/WebChromeClient.h | 3 +- .../WebProcess/cocoa/PlaybackSessionManager.h | 1 + .../cocoa/PlaybackSessionManager.messages.in | 1 + .../cocoa/PlaybackSessionManager.mm | 5 ++ .../cocoa/VideoPresentationManager.h | 1 + .../cocoa/VideoPresentationManager.mm | 14 +++++ .../LinearMediaKit.tbd | 4 ++ .../LinearMediaKit.tbd | 4 ++ 34 files changed, 324 insertions(+), 10 deletions(-) diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp index fe25a59430a7c..153c5a6b58427 100644 --- a/Source/WebCore/html/HTMLMediaElement.cpp +++ b/Source/WebCore/html/HTMLMediaElement.cpp @@ -7295,6 +7295,24 @@ bool HTMLMediaElement::videoUsesElementFullscreen() const return false; } +void HTMLMediaElement::setPlayerIdentifierForVideoElement() +{ + ALWAYS_LOG(LOGIDENTIFIER); + + RefPtr page = document().page(); + if (!page || page->mediaPlaybackIsSuspended()) + return; + + RefPtr window = document().domWindow(); + if (!window) + return; + + if (RefPtr asVideo = dynamicDowncast(*this)) { + auto& client = document().page()->chrome().client(); + client.setPlayerIdentifierForVideoElement(*asVideo); + } +} + void HTMLMediaElement::enterFullscreen(VideoFullscreenMode mode) { ALWAYS_LOG(LOGIDENTIFIER, ", m_videoFullscreenMode = ", m_videoFullscreenMode, ", mode = ", mode); diff --git a/Source/WebCore/html/HTMLMediaElement.h b/Source/WebCore/html/HTMLMediaElement.h index bab869cfc1bf0..533867ff00255 100644 --- a/Source/WebCore/html/HTMLMediaElement.h +++ b/Source/WebCore/html/HTMLMediaElement.h @@ -512,6 +512,7 @@ class HTMLMediaElement VideoFullscreenMode fullscreenMode() const { return m_videoFullscreenMode; } void enterFullscreen(VideoFullscreenMode); + WEBCORE_EXPORT void setPlayerIdentifierForVideoElement(); WEBCORE_EXPORT void enterFullscreen() override; WEBCORE_EXPORT void exitFullscreen(); WEBCORE_EXPORT void prepareForVideoFullscreenStandby(); diff --git a/Source/WebCore/page/ChromeClient.h b/Source/WebCore/page/ChromeClient.h index 4b2105d320fd2..3f5437b1e5dc6 100644 --- a/Source/WebCore/page/ChromeClient.h +++ b/Source/WebCore/page/ChromeClient.h @@ -472,6 +472,7 @@ class ChromeClient { #endif #if ENABLE(VIDEO) + virtual void setPlayerIdentifierForVideoElement(HTMLVideoElement&) { } virtual void enterVideoFullscreenForVideoElement(HTMLVideoElement&, HTMLMediaElementEnums::VideoFullscreenMode, bool standby) { UNUSED_PARAM(standby); } virtual void setUpPlaybackControlsManager(HTMLMediaElement&) { } virtual void clearPlaybackControlsManager() { } diff --git a/Source/WebCore/platform/cocoa/PlaybackSessionModel.h b/Source/WebCore/platform/cocoa/PlaybackSessionModel.h index 7b443bff0ce25..f4d7ed8578256 100644 --- a/Source/WebCore/platform/cocoa/PlaybackSessionModel.h +++ b/Source/WebCore/platform/cocoa/PlaybackSessionModel.h @@ -90,6 +90,7 @@ class PlaybackSessionModel : public CanMakeWeakPtr { virtual void togglePictureInPicture() = 0; virtual void enterInWindowFullscreen() = 0; virtual void exitInWindowFullscreen() = 0; + virtual void setPlayerIdentifierForVideoElement() = 0; virtual void enterFullscreen() = 0; virtual void exitFullscreen() = 0; virtual void toggleMuted() = 0; diff --git a/Source/WebCore/platform/cocoa/PlaybackSessionModelMediaElement.h b/Source/WebCore/platform/cocoa/PlaybackSessionModelMediaElement.h index 24c107f0d091f..0da74b3fa8df0 100644 --- a/Source/WebCore/platform/cocoa/PlaybackSessionModelMediaElement.h +++ b/Source/WebCore/platform/cocoa/PlaybackSessionModelMediaElement.h @@ -93,6 +93,7 @@ class PlaybackSessionModelMediaElement final WEBCORE_EXPORT void enterInWindowFullscreen() final; WEBCORE_EXPORT void exitInWindowFullscreen() final; WEBCORE_EXPORT void enterFullscreen() final; + WEBCORE_EXPORT void setPlayerIdentifierForVideoElement() final; WEBCORE_EXPORT void exitFullscreen() final; WEBCORE_EXPORT void toggleMuted() final; WEBCORE_EXPORT void setMuted(bool) final; diff --git a/Source/WebCore/platform/cocoa/PlaybackSessionModelMediaElement.mm b/Source/WebCore/platform/cocoa/PlaybackSessionModelMediaElement.mm index 0250af5da8d78..7eca562c2135c 100644 --- a/Source/WebCore/platform/cocoa/PlaybackSessionModelMediaElement.mm +++ b/Source/WebCore/platform/cocoa/PlaybackSessionModelMediaElement.mm @@ -423,6 +423,16 @@ element->enterFullscreenIgnoringPermissionsPolicy(); } +void PlaybackSessionModelMediaElement::setPlayerIdentifierForVideoElement() +{ + RefPtr element = dynamicDowncast(m_mediaElement); + ASSERT(element); + if (!element) + return; + + element->setPlayerIdentifierForVideoElement(); +} + void PlaybackSessionModelMediaElement::exitFullscreen() { RefPtr element = dynamicDowncast(m_mediaElement); diff --git a/Source/WebCore/platform/ios/PlaybackSessionInterfaceIOS.h b/Source/WebCore/platform/ios/PlaybackSessionInterfaceIOS.h index 497258a40c890..26ea40e954868 100644 --- a/Source/WebCore/platform/ios/PlaybackSessionInterfaceIOS.h +++ b/Source/WebCore/platform/ios/PlaybackSessionInterfaceIOS.h @@ -49,6 +49,7 @@ namespace WebCore { class IntRect; class PlaybackSessionModel; +class VideoPresentationInterfaceIOS; class WebPlaybackSessionChangeObserver; class WEBCORE_EXPORT PlaybackSessionInterfaceIOS @@ -81,6 +82,7 @@ class WEBCORE_EXPORT PlaybackSessionInterfaceIOS std::optional playerIdentifier() const; void setPlayerIdentifier(std::optional); + void setVideoPresentationInterface(WeakPtr); virtual void startObservingNowPlayingMetadata(); virtual void stopObservingNowPlayingMetadata(); @@ -108,6 +110,8 @@ class WEBCORE_EXPORT PlaybackSessionInterfaceIOS void incrementCheckedPtrCount() const final; void decrementCheckedPtrCount() const final; + WeakPtr m_videoPresentationInterface; + private: std::optional m_playerIdentifier; #if HAVE(SPATIAL_TRACKING_LABEL) diff --git a/Source/WebCore/platform/ios/PlaybackSessionInterfaceIOS.mm b/Source/WebCore/platform/ios/PlaybackSessionInterfaceIOS.mm index b5aef863baf96..03e6438f201ce 100644 --- a/Source/WebCore/platform/ios/PlaybackSessionInterfaceIOS.mm +++ b/Source/WebCore/platform/ios/PlaybackSessionInterfaceIOS.mm @@ -108,6 +108,11 @@ m_playerIdentifier = WTFMove(identifier); } +void PlaybackSessionInterfaceIOS::setVideoPresentationInterface(WeakPtr videoPresentationInterface) +{ + m_videoPresentationInterface = WTFMove(videoPresentationInterface); +} + void PlaybackSessionInterfaceIOS::startObservingNowPlayingMetadata() { } diff --git a/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.h b/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.h index a5363b3dbb5af..e6900a3c13f82 100644 --- a/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.h +++ b/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.h @@ -71,6 +71,8 @@ class VideoPresentationInterfaceIOS WTF_MAKE_TZONE_ALLOCATED_EXPORT(VideoPresentationInterfaceIOS, WEBCORE_EXPORT); WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(VideoPresentationInterfaceIOS); public: + USING_CAN_MAKE_WEAKPTR(VideoPresentationModelClient); + WEBCORE_EXPORT ~VideoPresentationInterfaceIOS(); // CheckedPtr interface @@ -103,6 +105,8 @@ class VideoPresentationInterfaceIOS WEBCORE_EXPORT void preparedToReturnToStandby(); bool changingStandbyOnly() { return m_changingStandbyOnly; } WEBCORE_EXPORT void failedToRestoreFullscreen(); + WEBCORE_EXPORT virtual void enterExternalPlayback(CompletionHandler&&); + WEBCORE_EXPORT virtual void exitExternalPlayback(CompletionHandler&&); enum class ExitFullScreenReason { DoneButtonTapped, @@ -218,6 +222,7 @@ class VideoPresentationInterfaceIOS virtual void updateRouteSharingPolicy() = 0; virtual void setupPlayerViewController() = 0; virtual void invalidatePlayerViewController() = 0; + virtual bool cleanupExternalPlayback() { return false; } virtual UIViewController *playerViewController() const = 0; WEBCORE_EXPORT void doSetup(); diff --git a/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm b/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm index 8517a53859c37..47e060079efdf 100644 --- a/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm +++ b/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm @@ -121,6 +121,7 @@ @interface UIViewController () : m_watchdogTimer(RunLoop::main(), this, &VideoPresentationInterfaceIOS::watchdogTimerFired) , m_playbackSessionInterface(playbackSessionInterface) { + m_playbackSessionInterface->setVideoPresentationInterface(this); } VideoPresentationInterfaceIOS::~VideoPresentationInterfaceIOS() @@ -369,6 +370,16 @@ @interface UIViewController () [playerLayerView() setHidden:enabled]; } +void VideoPresentationInterfaceIOS::enterExternalPlayback(CompletionHandler&& completionHandler) +{ + completionHandler(false, nil); +} + +void VideoPresentationInterfaceIOS::exitExternalPlayback(CompletionHandler&& completionHandler) +{ + completionHandler(false); +} + void VideoPresentationInterfaceIOS::setInlineRect(const FloatRect& inlineRect, bool visible) { m_inlineRect = inlineRect; @@ -583,6 +594,12 @@ @interface UIViewController () void VideoPresentationInterfaceIOS::cleanupFullscreen() { LOG(Fullscreen, "VideoPresentationInterfaceIOS::cleanupFullscreen(%p)", this); + + if (this->cleanupExternalPlayback()) { + ASSERT(m_currentMode == HTMLMediaElementEnums::VideoFullscreenModeNone); + return; + } + m_shouldIgnoreAVKitCallbackAboutExitFullscreenReason = false; m_cleanupNeedsReturnVideoContentLayer = true; diff --git a/Source/WebCore/platform/ios/WebVideoFullscreenControllerAVKit.mm b/Source/WebCore/platform/ios/WebVideoFullscreenControllerAVKit.mm index 3437ae9a0a5e9..c424bb38ad6f8 100644 --- a/Source/WebCore/platform/ios/WebVideoFullscreenControllerAVKit.mm +++ b/Source/WebCore/platform/ios/WebVideoFullscreenControllerAVKit.mm @@ -175,6 +175,7 @@ void togglePictureInPicture() override { } void enterInWindowFullscreen() override { } void exitInWindowFullscreen() override { } void enterFullscreen() override { } + void setPlayerIdentifierForVideoElement() final { } void toggleMuted() override; void setMuted(bool) final; void setVolume(double) final; diff --git a/Source/WebKit/Platform/ios/PlaybackSessionInterfaceLMK.mm b/Source/WebKit/Platform/ios/PlaybackSessionInterfaceLMK.mm index 1212db3d6fd78..8bd4dd0c45b2c 100644 --- a/Source/WebKit/Platform/ios/PlaybackSessionInterfaceLMK.mm +++ b/Source/WebKit/Platform/ios/PlaybackSessionInterfaceLMK.mm @@ -28,6 +28,7 @@ #if ENABLE(LINEAR_MEDIA_PLAYER) +#import "VideoPresentationInterfaceLMK.h" #import "WKSLinearMediaPlayer.h" #import "WKSLinearMediaTypes.h" #import @@ -215,6 +216,12 @@ - (void)linearMediaPlayer:(WKSLinearMediaPlayer *)player setVideoReceiverEndpoin model->setVideoReceiverEndpoint(videoReceiverEndpoint); } +- (void)linearMediaPlayerClearVideoReceiverEndpoint:(WKSLinearMediaPlayer *)player +{ + if (auto model = _model.get()) + model->setVideoReceiverEndpoint(nullptr); +} + @end namespace WebKit { @@ -358,6 +365,12 @@ - (void)linearMediaPlayer:(WKSLinearMediaPlayer *)player setVideoReceiverEndpoin if (m_playbackSessionModel) m_playbackSessionModel->exitFullscreen(); break; + case WKSLinearMediaPresentationStateExternal: + // If the player is in external presentation (which uses LinearMediaPlayer) but the current + // media engine does not support it, exit external presentation. + if (RefPtr videoPresentationInterface = m_videoPresentationInterface.get()) + videoPresentationInterface->exitExternalPlayback([](bool) { }); + break; case WKSLinearMediaPresentationStateInline: case WKSLinearMediaPresentationStateExitingFullscreen: break; diff --git a/Source/WebKit/Platform/ios/VideoPresentationInterfaceLMK.h b/Source/WebKit/Platform/ios/VideoPresentationInterfaceLMK.h index 6fe2a5ea246d4..601ffcc528fe0 100644 --- a/Source/WebKit/Platform/ios/VideoPresentationInterfaceLMK.h +++ b/Source/WebKit/Platform/ios/VideoPresentationInterfaceLMK.h @@ -65,6 +65,8 @@ class VideoPresentationInterfaceLMK final : public WebCore::VideoPresentationInt void setupPlayerViewController() final; void invalidatePlayerViewController() final; UIViewController *playerViewController() const final; + void enterExternalPlayback(CompletionHandler &&) final; + void exitExternalPlayback(CompletionHandler&&) final; void tryToStartPictureInPicture() final { } void stopPictureInPicture() final { } void presentFullscreen(bool animated, Function&&) final; @@ -73,6 +75,7 @@ class VideoPresentationInterfaceLMK final : public WebCore::VideoPresentationInt void setContentDimensions(const WebCore::FloatSize&) final; void setAllowsPictureInPicturePlayback(bool) final { } bool isExternalPlaybackActive() const final { return false; } + bool cleanupExternalPlayback() final; bool willRenderToLayer() const final { return false; } AVPlayerViewController *avPlayerViewController() const final { return nullptr; } CALayer *captionsLayer() final; diff --git a/Source/WebKit/Platform/ios/VideoPresentationInterfaceLMK.mm b/Source/WebKit/Platform/ios/VideoPresentationInterfaceLMK.mm index aff5807adc1d6..caa59754eeb10 100644 --- a/Source/WebKit/Platform/ios/VideoPresentationInterfaceLMK.mm +++ b/Source/WebKit/Platform/ios/VideoPresentationInterfaceLMK.mm @@ -156,6 +156,57 @@ - (void)layoutSublayers }).get()]; } +void VideoPresentationInterfaceLMK::enterExternalPlayback(CompletionHandler&& completionHandler) +{ + ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER); + + if (linearMediaPlayer().presentationState != WKSLinearMediaPresentationStateInline) { + completionHandler(false, nil); + return; + } + setupPlayerViewController(); + + playbackSessionInterface().startObservingNowPlayingMetadata(); + [linearMediaPlayer() enterExternalPresentationWithCompletionHandler:makeBlockPtr([this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler)] (BOOL success, NSError *error) mutable { + if (auto* playbackSessionModel = this->playbackSessionModel()) { + playbackSessionModel->setSpatialTrackingLabel(m_spatialTrackingLabel); + playbackSessionModel->setSoundStageSize(WebCore::AudioSessionSoundStageSize::Large); + } + completionHandler(success, m_playerViewController.get()); + }).get()]; +} + +void VideoPresentationInterfaceLMK::exitExternalPlayback(CompletionHandler&& completionHandler) +{ + ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER); + + if (linearMediaPlayer().presentationState != WKSLinearMediaPresentationStateExternal) { + completionHandler(false); + return; + } + + playbackSessionInterface().stopObservingNowPlayingMetadata(); + [linearMediaPlayer() exitExternalPresentationWithCompletionHandler:makeBlockPtr([this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler)] (BOOL success, NSError *error) mutable { + if (auto* playbackSessionModel = this->playbackSessionModel()) { + playbackSessionModel->setSpatialTrackingLabel(nullString()); + playbackSessionModel->setSoundStageSize(WebCore::AudioSessionSoundStageSize::Automatic); + } + invalidatePlayerViewController(); + completionHandler(success); + }).get()]; +} + +bool VideoPresentationInterfaceLMK::cleanupExternalPlayback() +{ + if (linearMediaPlayer().presentationState != WKSLinearMediaPresentationStateExternal) + return false; + + ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER); + + exitExternalPlayback([](bool) { }); + return true; +} + UIViewController *VideoPresentationInterfaceLMK::playerViewController() const { return m_playerViewController.get(); diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h b/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h index fb3900d838d77..1250f7ecf0faf 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h @@ -760,6 +760,9 @@ typedef NS_OPTIONS(NSUInteger, _WKWebViewDataType) { #if TARGET_OS_VISION @interface WKWebView (WKPrivateVision) @property (copy, setter=_setDefaultSTSLabel:) NSString *_defaultSTSLabel; + +- (void)_enterExternalPlaybackForNowPlayingMediaSessionWithCompletionHandler:(void (^)(UIViewController *nowPlayingViewController, NSError *error))completionHandler WK_API_AVAILABLE(visionos(WK_XROS_TBA)); +- (void)_exitExternalPlaybackWithCompletionHandler:(void (^)(NSError *error))completionHandler WK_API_AVAILABLE(visionos(WK_XROS_TBA)); @end #endif diff --git a/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm b/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm index 8a2efd8678929..28f546f015e41 100644 --- a/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm +++ b/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm @@ -47,6 +47,7 @@ #import "WKBackForwardListItemInternal.h" #import "WKContentViewInteraction.h" #import "WKDataDetectorTypesInternal.h" +#import "WKErrorInternal.h" #import "WKPasswordView.h" #import "WKProcessPoolPrivate.h" #import "WKScrollView.h" @@ -4942,6 +4943,34 @@ - (void)_setDefaultSTSLabel:(NSString *)defaultSTSLabel { _page->setDefaultSpatialTrackingLabel(defaultSTSLabel); } + +- (void)_enterExternalPlaybackForNowPlayingMediaSessionWithCompletionHandler:(void(^)(UIViewController *, NSError *))completionHandler +{ + if (!_page) { + completionHandler(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nil]); + return; + } + + _page->setPlayerIdentifierForVideoElement(); + _page->enterExternalPlaybackForNowPlayingMediaSession([handler = makeBlockPtr(completionHandler)](bool entered, UIViewController *viewController) { + if (entered) + handler(viewController, nil); + else + handler(nil, createNSError(WKErrorUnknown).get()); + }); +} + +- (void)_exitExternalPlaybackWithCompletionHandler:(void (^)(NSError *error))completionHandler +{ + if (!_page) { + completionHandler([NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nil]); + return; + } + + _page->exitExternalPlayback([handler = makeBlockPtr(completionHandler)](bool success) { + handler(success ? nil : createNSError(WKErrorUnknown).get()); + }); +} @end #endif diff --git a/Source/WebKit/UIProcess/Cocoa/PlaybackSessionManagerProxy.h b/Source/WebKit/UIProcess/Cocoa/PlaybackSessionManagerProxy.h index 9ffc8ae47e4ab..0a30f69f6254e 100644 --- a/Source/WebKit/UIProcess/Cocoa/PlaybackSessionManagerProxy.h +++ b/Source/WebKit/UIProcess/Cocoa/PlaybackSessionManagerProxy.h @@ -131,6 +131,7 @@ class PlaybackSessionModelContext final void enterInWindowFullscreen() final; void exitInWindowFullscreen() final; void enterFullscreen() final; + void setPlayerIdentifierForVideoElement() final; void exitFullscreen() final; void toggleMuted() final; void setMuted(bool) final; @@ -320,6 +321,7 @@ class PlaybackSessionManagerProxy void selectLegibleMediaOption(PlaybackSessionContextIdentifier, uint64_t index); void togglePictureInPicture(PlaybackSessionContextIdentifier); void enterFullscreen(PlaybackSessionContextIdentifier); + void setPlayerIdentifierForVideoElement(PlaybackSessionContextIdentifier); void exitFullscreen(PlaybackSessionContextIdentifier); void enterInWindow(PlaybackSessionContextIdentifier); void exitInWindow(PlaybackSessionContextIdentifier); diff --git a/Source/WebKit/UIProcess/Cocoa/PlaybackSessionManagerProxy.mm b/Source/WebKit/UIProcess/Cocoa/PlaybackSessionManagerProxy.mm index b94f1e81e5453..ebd5650780b54 100644 --- a/Source/WebKit/UIProcess/Cocoa/PlaybackSessionManagerProxy.mm +++ b/Source/WebKit/UIProcess/Cocoa/PlaybackSessionManagerProxy.mm @@ -256,6 +256,13 @@ manager->enterFullscreen(m_contextId); } +void PlaybackSessionModelContext::setPlayerIdentifierForVideoElement() +{ + ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER); + if (RefPtr manager = m_manager.get()) + manager->setPlayerIdentifierForVideoElement(m_contextId); +} + void PlaybackSessionModelContext::exitFullscreen() { ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER); @@ -915,6 +922,12 @@ page->protectedLegacyMainFrameProcess()->send(Messages::PlaybackSessionManager::EnterFullscreen(contextId), page->webPageIDInMainFrameProcess()); } +void PlaybackSessionManagerProxy::setPlayerIdentifierForVideoElement(PlaybackSessionContextIdentifier contextId) +{ + if (RefPtr page = m_page.get()) + page->protectedLegacyMainFrameProcess()->send(Messages::PlaybackSessionManager::SetPlayerIdentifierForVideoElement(contextId), page->webPageIDInMainFrameProcess()); +} + void PlaybackSessionManagerProxy::exitFullscreen(PlaybackSessionContextIdentifier contextId) { if (RefPtr page = m_page.get()) diff --git a/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm index f82370a7b2ec9..c0c9d8098ce76 100644 --- a/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm +++ b/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm @@ -86,6 +86,7 @@ #import #import #import +#import #import #import #import @@ -632,6 +633,40 @@ static bool exceedsRenderTreeSizeSizeThreshold(uint64_t thresholdSize, uint64_t } #endif +#if PLATFORM(VISION) +void WebPageProxy::enterExternalPlaybackForNowPlayingMediaSession(CompletionHandler&& completionHandler) +{ + if (!m_videoPresentationManager) { + completionHandler(false, nil); + return; + } + + RefPtr videoPresentationInterface = m_videoPresentationManager->controlsManagerInterface(); + if (!videoPresentationInterface) { + completionHandler(false, nil); + return; + } + + videoPresentationInterface->enterExternalPlayback(WTFMove(completionHandler)); +} + +void WebPageProxy::exitExternalPlayback(CompletionHandler&& completionHandler) +{ + if (!m_videoPresentationManager) { + completionHandler(false); + return; + } + + RefPtr videoPresentationInterface = m_videoPresentationManager->controlsManagerInterface(); + if (!videoPresentationInterface) { + completionHandler(false); + return; + } + + videoPresentationInterface->exitExternalPlayback(WTFMove(completionHandler)); +} +#endif + #if ENABLE(VIDEO_PRESENTATION_MODE) void WebPageProxy::didChangePlaybackRate(PlaybackSessionContextIdentifier identifier) @@ -695,7 +730,6 @@ static bool exceedsRenderTreeSizeSizeThreshold(uint64_t thresholdSize, uint64_t #endif }); } - #endif // ENABLE(VIDEO_PRESENTATION_MODE) #if HAVE(QUICKLOOK_THUMBNAILING) diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp index 92648c01bfce9..e31ed810cb6dd 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.cpp +++ b/Source/WebKit/UIProcess/WebPageProxy.cpp @@ -8562,6 +8562,23 @@ void WebPageProxy::enterFullscreen() playbackSessionModel->enterFullscreen(); } +void WebPageProxy::setPlayerIdentifierForVideoElement() +{ + RefPtr playbackSessionManager = m_playbackSessionManager; + if (!playbackSessionManager) + return; + + RefPtr controlsManagerInterface = playbackSessionManager->controlsManagerInterface(); + if (!controlsManagerInterface) + return; + + CheckedPtr playbackSessionModel = controlsManagerInterface->playbackSessionModel(); + if (!playbackSessionModel) + return; + + playbackSessionModel->setPlayerIdentifierForVideoElement(); +} + void WebPageProxy::didEnterFullscreen(PlaybackSessionContextIdentifier identifier) { if (RefPtr pageClient = this->pageClient()) diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h index deb82e0de6d58..ededc67599999 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.h +++ b/Source/WebKit/UIProcess/WebPageProxy.h @@ -398,6 +398,7 @@ OBJC_CLASS WKQLThumbnailLoadOperation; OBJC_CLASS WKQuickLookPreviewController; OBJC_CLASS WKWebView; OBJC_CLASS _WKRemoteObjectRegistry; +OBJC_CLASS UIViewController; struct WKPageInjectedBundleClientBase; struct wpe_view_backend; @@ -2295,7 +2296,13 @@ class WebPageProxy final : public API::ObjectImpl, publ void removeMediaUsageManagerSession(WebCore::MediaSessionIdentifier); #endif +#if PLATFORM(VISION) + void enterExternalPlaybackForNowPlayingMediaSession(CompletionHandler&&); + void exitExternalPlayback(CompletionHandler&&); +#endif + #if ENABLE(VIDEO_PRESENTATION_MODE) + void setPlayerIdentifierForVideoElement(); bool canEnterFullscreen(); void enterFullscreen(); diff --git a/Source/WebKit/WebKitSwift/LinearMediaKit/LinearMediaPlayer.swift b/Source/WebKit/WebKitSwift/LinearMediaKit/LinearMediaPlayer.swift index d2400c8ef43fc..e843d62ff09ad 100644 --- a/Source/WebKit/WebKitSwift/LinearMediaKit/LinearMediaPlayer.swift +++ b/Source/WebKit/WebKitSwift/LinearMediaKit/LinearMediaPlayer.swift @@ -49,6 +49,9 @@ private class SwiftOnlyData: NSObject { @Published var presentationMode: PresentationMode = .inline @Published var presentationState: WKSLinearMediaPresentationState = .inline + // FIXME: Publish fullscreenSceneBehaviors once rdar://122435030 is resolved + var fullscreenBehaviorsSubject = CurrentValueSubject<[FullscreenBehaviors], Never>(FullscreenBehaviors.default) + // Will be set to true if we entered via Docking Environment button (inline) or false otherwise. var enteredFromInline = false @@ -117,7 +120,6 @@ enum LinearMediaPlayerErrors: Error { var allowFullScreenFromInline = true var isLiveStream = false var recommendedViewingRatio: NSNumber? - var fullscreenSceneBehaviors: WKSLinearMediaFullscreenBehaviors = [] var startTime: Double = .nan var endTime: Double = .nan var spatialImmersive = false @@ -193,6 +195,37 @@ enum LinearMediaPlayerErrors: Error { viewController.prefersAutoDimming = true return viewController } + + func enterExternalPresentation(completionHandler: @escaping (Bool, (any Error)?) -> Void) { + Logger.linearMediaPlayer.log("\(#function)") + + switch presentationState { + case .enteringFullscreen, .exitingFullscreen, .fullscreen, .external: + completionHandler(false, LinearMediaPlayerErrors.invalidStateError) + case .inline: + swiftOnlyData.presentationState = .external + swiftOnlyData.fullscreenBehaviorsSubject.send([ .hostContentInline ]) + completionHandler(true, nil) + @unknown default: + fatalError() + } + } + + func exitExternalPresentation(completionHandler: @escaping (Bool, (any Error)?) -> Void) { + Logger.linearMediaPlayer.log("\(#function)") + + switch presentationState { + case .enteringFullscreen, .exitingFullscreen, .fullscreen, .inline: + completionHandler(false, LinearMediaPlayerErrors.invalidStateError) + case .external: + delegate?.linearMediaPlayerClearVideoReceiverEndpoint?(self) + swiftOnlyData.presentationState = .inline + swiftOnlyData.fullscreenBehaviorsSubject.send(FullscreenBehaviors.default) + completionHandler(true, nil) + @unknown default: + fatalError() + } + } func enterFullscreen(completionHandler: @escaping (Bool, (any Error)?) -> Void) { Logger.linearMediaPlayer.log("\(#function)") @@ -211,6 +244,8 @@ enum LinearMediaPlayerErrors: Error { swiftOnlyData.presentationState = .fullscreen case .fullscreen: completionHandler(true, nil) + case .external: + completionHandler(false, LinearMediaPlayerErrors.invalidStateError) @unknown default: fatalError() } @@ -231,6 +266,8 @@ enum LinearMediaPlayerErrors: Error { swiftOnlyData.presentationState = .inline case .inline: completionHandler(true, nil) + case .external: + completionHandler(false, LinearMediaPlayerErrors.invalidStateError) @unknown default: fatalError() } @@ -246,7 +283,7 @@ extension WKSLinearMediaPlayer { swiftOnlyData.presentationMode = .inline case .enteringFullscreen: delegate?.linearMediaPlayerEnterFullscreen?(self) - case .fullscreen: + case .fullscreen, .external: swiftOnlyData.presentationMode = .fullscreenFromInline case .exitingFullscreen: delegate?.linearMediaPlayerExitFullscreen?(self) @@ -538,7 +575,7 @@ extension WKSLinearMediaPlayer: @retroactive Playable { public var fullscreenSceneBehaviorsPublisher: AnyPublisher<[FullscreenBehaviors], Never> { // FIXME: Publish fullscreenSceneBehaviors once rdar://122435030 is resolved - Just(FullscreenBehaviors.default).eraseToAnyPublisher() + swiftOnlyData.fullscreenBehaviorsSubject.eraseToAnyPublisher() } public func updateRenderingConfiguration(_ config: RenderingConfiguration) { @@ -649,7 +686,7 @@ extension WKSLinearMediaPlayer: @retroactive Playable { swiftOnlyData.presentationState = .enteringFullscreen case .fullscreen: swiftOnlyData.presentationState = .exitingFullscreen - case .enteringFullscreen, .exitingFullscreen: + case .enteringFullscreen, .exitingFullscreen, .external: break @unknown default: fatalError() @@ -663,7 +700,7 @@ extension WKSLinearMediaPlayer: @retroactive Playable { case .inline: swiftOnlyData.enteredFromInline = true swiftOnlyData.presentationState = .enteringFullscreen - case .enteringFullscreen, .exitingFullscreen, .fullscreen: + case .enteringFullscreen, .exitingFullscreen, .fullscreen, .external: break @unknown default: fatalError() @@ -690,7 +727,7 @@ extension WKSLinearMediaPlayer: @retroactive Playable { switch presentationState { case .fullscreen: swiftOnlyData.presentationState = .exitingFullscreen - case .inline, .enteringFullscreen, .exitingFullscreen: + case .inline, .enteringFullscreen, .exitingFullscreen, .external: break @unknown default: fatalError() diff --git a/Source/WebKit/WebKitSwift/LinearMediaKit/LinearMediaTypes.swift b/Source/WebKit/WebKitSwift/LinearMediaKit/LinearMediaTypes.swift index 758eb4bd61f07..af600d305e9a9 100644 --- a/Source/WebKit/WebKitSwift/LinearMediaKit/LinearMediaTypes.swift +++ b/Source/WebKit/WebKitSwift/LinearMediaKit/LinearMediaTypes.swift @@ -149,6 +149,8 @@ extension WKSLinearMediaPresentationState: CustomStringConvertible { return "fullscreen" case .exitingFullscreen: return "exitingFullscreen" + case .external: + return "external" @unknown default: fatalError() } diff --git a/Source/WebKit/WebKitSwift/LinearMediaKit/WKSLinearMediaPlayer.h b/Source/WebKit/WebKitSwift/LinearMediaKit/WKSLinearMediaPlayer.h index b37d7a8b1cca8..4f7dd291e4bc2 100644 --- a/Source/WebKit/WebKitSwift/LinearMediaKit/WKSLinearMediaPlayer.h +++ b/Source/WebKit/WebKitSwift/LinearMediaKit/WKSLinearMediaPlayer.h @@ -95,6 +95,7 @@ API_AVAILABLE(visionos(1.0)) - (void)linearMediaPlayer:(WKSLinearMediaPlayer *)player setThumbnailSize:(CGSize)size; - (void)linearMediaPlayer:(WKSLinearMediaPlayer *)player seekThumbnailToTime:(NSTimeInterval)time; - (void)linearMediaPlayer:(WKSLinearMediaPlayer *)player setVideoReceiverEndpoint:(xpc_object_t)videoReceiverEndpoint; +- (void)linearMediaPlayerClearVideoReceiverEndpoint:(WKSLinearMediaPlayer *)player; @end API_AVAILABLE(visionos(1.0)) @@ -152,7 +153,6 @@ API_AVAILABLE(visionos(1.0)) @property (nonatomic) BOOL allowFullScreenFromInline; @property (nonatomic) BOOL isLiveStream; @property (nonatomic, strong, nullable) NSNumber *recommendedViewingRatio; -@property (nonatomic) WKSLinearMediaFullscreenBehaviors fullscreenSceneBehaviors; @property (nonatomic) double startTime; @property (nonatomic) double endTime; @property (nonatomic, strong, nullable) NSDate *startDate; @@ -165,6 +165,8 @@ API_AVAILABLE(visionos(1.0)) - (LMPlayableViewController *)makeViewController; - (void)enterFullscreenWithCompletionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler; - (void)exitFullscreenWithCompletionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler; +- (void)enterExternalPresentationWithCompletionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler; +- (void)exitExternalPresentationWithCompletionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler; @end NS_ASSUME_NONNULL_END diff --git a/Source/WebKit/WebKitSwift/LinearMediaKit/WKSLinearMediaTypes.h b/Source/WebKit/WebKitSwift/LinearMediaKit/WKSLinearMediaTypes.h index a2c79e7f25bae..719070abaff8d 100644 --- a/Source/WebKit/WebKitSwift/LinearMediaKit/WKSLinearMediaTypes.h +++ b/Source/WebKit/WebKitSwift/LinearMediaKit/WKSLinearMediaTypes.h @@ -48,7 +48,8 @@ typedef NS_ENUM(NSInteger, WKSLinearMediaPresentationState) { WKSLinearMediaPresentationStateInline = 0, WKSLinearMediaPresentationStateEnteringFullscreen, WKSLinearMediaPresentationStateFullscreen, - WKSLinearMediaPresentationStateExitingFullscreen + WKSLinearMediaPresentationStateExitingFullscreen, + WKSLinearMediaPresentationStateExternal }; typedef NS_ENUM(NSInteger, WKSLinearMediaViewingMode) { diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp index 4951f5542891a..16f0f27a7e2b3 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp @@ -1264,6 +1264,11 @@ void WebChromeClient::enterVideoFullscreenForVideoElement(HTMLVideoElement& vide protectedPage()->videoPresentationManager().enterVideoFullscreenForVideoElement(videoElement, mode, standby); } +void WebChromeClient::setPlayerIdentifierForVideoElement(HTMLVideoElement& videoElement) +{ + protectedPage()->videoPresentationManager().setPlayerIdentifierForVideoElement(videoElement); +} + void WebChromeClient::exitVideoFullscreenForVideoElement(HTMLVideoElement& videoElement, CompletionHandler&& completionHandler) { protectedPage()->videoPresentationManager().exitVideoFullscreenForVideoElement(videoElement, WTFMove(completionHandler)); diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h index 6f2c1b862320a..2d3795405f879 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h @@ -314,7 +314,8 @@ class WebChromeClient final : public WebCore::ChromeClient { bool supportsVideoFullscreen(WebCore::HTMLMediaElementEnums::VideoFullscreenMode) final; bool supportsVideoFullscreenStandby() final; void setMockVideoPresentationModeEnabled(bool) final; - void enterVideoFullscreenForVideoElement(WebCore::HTMLVideoElement&, WebCore::HTMLMediaElementEnums::VideoFullscreenMode, bool standby) final; + void enterVideoFullscreenForVideoElement(WebCore::HTMLVideoElement&, WebCore::HTMLMediaElementEnums::VideoFullscreenMode, bool) final; + void setPlayerIdentifierForVideoElement(WebCore::HTMLVideoElement&) final; void exitVideoFullscreenForVideoElement(WebCore::HTMLVideoElement&, WTF::CompletionHandler&& = [](bool) { }) final; void setUpPlaybackControlsManager(WebCore::HTMLMediaElement&) final; void clearPlaybackControlsManager() final; diff --git a/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.h b/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.h index 359ae90315280..0e55c8d39c102 100644 --- a/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.h +++ b/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.h @@ -191,6 +191,7 @@ class PlaybackSessionManager : public RefCounted, privat void handleControlledElementIDRequest(PlaybackSessionContextIdentifier); void togglePictureInPicture(PlaybackSessionContextIdentifier); void enterFullscreen(PlaybackSessionContextIdentifier); + void setPlayerIdentifierForVideoElement(PlaybackSessionContextIdentifier); void exitFullscreen(PlaybackSessionContextIdentifier); void enterInWindow(PlaybackSessionContextIdentifier); void exitInWindow(PlaybackSessionContextIdentifier); diff --git a/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.messages.in b/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.messages.in index 56b6b27f1f94a..0a406bb61c912 100644 --- a/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.messages.in +++ b/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.messages.in @@ -44,6 +44,7 @@ messages -> PlaybackSessionManager { HandleControlledElementIDRequest(WebKit::PlaybackSessionContextIdentifier contextId) TogglePictureInPicture(WebKit::PlaybackSessionContextIdentifier contextId) EnterFullscreen(WebKit::PlaybackSessionContextIdentifier contextId) + SetPlayerIdentifierForVideoElement(WebKit::PlaybackSessionContextIdentifier contextId) ExitFullscreen(WebKit::PlaybackSessionContextIdentifier contextId) EnterInWindow(WebKit::PlaybackSessionContextIdentifier contextId) ExitInWindow(WebKit::PlaybackSessionContextIdentifier contextId) diff --git a/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.mm b/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.mm index f1052f0b37e77..a3336e60e3cfc 100644 --- a/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.mm +++ b/Source/WebKit/WebProcess/cocoa/PlaybackSessionManager.mm @@ -594,6 +594,11 @@ ensureModel(contextId)->enterFullscreen(); } +void PlaybackSessionManager::setPlayerIdentifierForVideoElement(PlaybackSessionContextIdentifier contextId) +{ + ensureModel(contextId)->setPlayerIdentifierForVideoElement(); +} + void PlaybackSessionManager::exitFullscreen(PlaybackSessionContextIdentifier contextId) { ensureModel(contextId)->exitFullscreen(); diff --git a/Source/WebKit/WebProcess/cocoa/VideoPresentationManager.h b/Source/WebKit/WebProcess/cocoa/VideoPresentationManager.h index b646539cf960b..05ef0395a0c53 100644 --- a/Source/WebKit/WebProcess/cocoa/VideoPresentationManager.h +++ b/Source/WebKit/WebProcess/cocoa/VideoPresentationManager.h @@ -157,6 +157,7 @@ class VideoPresentationManager bool supportsVideoFullscreen(WebCore::HTMLMediaElementEnums::VideoFullscreenMode) const; bool supportsVideoFullscreenStandby() const; void enterVideoFullscreenForVideoElement(WebCore::HTMLVideoElement&, WebCore::HTMLMediaElementEnums::VideoFullscreenMode, bool standby); + void setPlayerIdentifierForVideoElement(WebCore::HTMLVideoElement&); void exitVideoFullscreenForVideoElement(WebCore::HTMLVideoElement&, WTF::CompletionHandler&& = [](bool) { }); void exitVideoFullscreenToModeWithoutAnimation(WebCore::HTMLVideoElement&, WebCore::HTMLMediaElementEnums::VideoFullscreenMode); void setVideoFullscreenMode(WebCore::HTMLVideoElement&, WebCore::HTMLMediaElementEnums::VideoFullscreenMode); diff --git a/Source/WebKit/WebProcess/cocoa/VideoPresentationManager.mm b/Source/WebKit/WebProcess/cocoa/VideoPresentationManager.mm index b750b44a7cb0b..febf221ee5a83 100644 --- a/Source/WebKit/WebProcess/cocoa/VideoPresentationManager.mm +++ b/Source/WebKit/WebProcess/cocoa/VideoPresentationManager.mm @@ -324,6 +324,20 @@ static FloatRect inlineVideoFrame(HTMLVideoElement& element) removeClientForContext(contextId); } +void VideoPresentationManager::setPlayerIdentifierForVideoElement(HTMLVideoElement& videoElement) +{ + RefPtr player = videoElement.player(); + if (!player) + return; + + auto identifier = player->identifier(); + if (!identifier) + return; + + auto contextId = m_playbackSessionManager->contextIdForMediaElement(videoElement); + setPlayerIdentifier(contextId, identifier); +} + void VideoPresentationManager::swapFullscreenModes(WebCore::HTMLVideoElement& firstElement, WebCore::HTMLVideoElement& secondElement) { auto firstContextId = m_playbackSessionManager->contextIdForMediaElement(firstElement); diff --git a/WebKitLibraries/SDKs/xros2.0-additions.sdk/System/Library/PrivateFrameworks/LinearMediaKit.framework/LinearMediaKit.tbd b/WebKitLibraries/SDKs/xros2.0-additions.sdk/System/Library/PrivateFrameworks/LinearMediaKit.framework/LinearMediaKit.tbd index 412fc57094508..7823e320e5124 100644 --- a/WebKitLibraries/SDKs/xros2.0-additions.sdk/System/Library/PrivateFrameworks/LinearMediaKit.framework/LinearMediaKit.tbd +++ b/WebKitLibraries/SDKs/xros2.0-additions.sdk/System/Library/PrivateFrameworks/LinearMediaKit.framework/LinearMediaKit.tbd @@ -19,6 +19,10 @@ exports: _$s14LinearMediaKit15ContentMetadataV15displaySubtitleAC07DisplayG0Ovg, _$s14LinearMediaKit15ContentMetadataV15displaySubtitleAC07DisplayG0OvpMV, _$s14LinearMediaKit15ContentMetadataVMa, _$s14LinearMediaKit15ContentMetadataVMn, _$s14LinearMediaKit16PresentationModeO20fullscreenFromInlineyA2CmFWC, _$s14LinearMediaKit16PresentationModeO6inlineyA2CmFWC, _$s14LinearMediaKit16PresentationModeOMa, _$s14LinearMediaKit16PresentationModeOMn, + _$s14LinearMediaKit19FullscreenBehaviorsV11sceneResizeACvgZ, + _$s14LinearMediaKit19FullscreenBehaviorsV17hostContentInlineACvgZ, + _$s14LinearMediaKit19FullscreenBehaviorsV18sceneChromeOptionsACvgZ, + _$s14LinearMediaKit19FullscreenBehaviorsV21sceneSizeRestrictionsACvgZ, _$s14LinearMediaKit19FullscreenBehaviorsV7defaultSayACGvgZ, _$s14LinearMediaKit19FullscreenBehaviorsV8rawValueACSi_tcfC, _$s14LinearMediaKit19FullscreenBehaviorsV8rawValueSivg, _$s14LinearMediaKit19FullscreenBehaviorsVMa, _$s14LinearMediaKit19FullscreenBehaviorsVMn, _$s14LinearMediaKit22PlayableViewControllerC023environmentPickerButtoneF0So06UIViewF0CSgvgTj, _$s14LinearMediaKit22PlayableViewControllerC18prefersAutoDimmingSbvsTj, diff --git a/WebKitLibraries/SDKs/xrsimulator2.0-additions.sdk/System/Library/PrivateFrameworks/LinearMediaKit.framework/LinearMediaKit.tbd b/WebKitLibraries/SDKs/xrsimulator2.0-additions.sdk/System/Library/PrivateFrameworks/LinearMediaKit.framework/LinearMediaKit.tbd index 8a4616c704f30..af5c84d94af5e 100644 --- a/WebKitLibraries/SDKs/xrsimulator2.0-additions.sdk/System/Library/PrivateFrameworks/LinearMediaKit.framework/LinearMediaKit.tbd +++ b/WebKitLibraries/SDKs/xrsimulator2.0-additions.sdk/System/Library/PrivateFrameworks/LinearMediaKit.framework/LinearMediaKit.tbd @@ -19,6 +19,10 @@ exports: _$s14LinearMediaKit15ContentMetadataV15displaySubtitleAC07DisplayG0Ovg, _$s14LinearMediaKit15ContentMetadataV15displaySubtitleAC07DisplayG0OvpMV, _$s14LinearMediaKit15ContentMetadataVMa, _$s14LinearMediaKit15ContentMetadataVMn, _$s14LinearMediaKit16PresentationModeO20fullscreenFromInlineyA2CmFWC, _$s14LinearMediaKit16PresentationModeO6inlineyA2CmFWC, _$s14LinearMediaKit16PresentationModeOMa, _$s14LinearMediaKit16PresentationModeOMn, + _$s14LinearMediaKit19FullscreenBehaviorsV11sceneResizeACvgZ, + _$s14LinearMediaKit19FullscreenBehaviorsV17hostContentInlineACvgZ, + _$s14LinearMediaKit19FullscreenBehaviorsV18sceneChromeOptionsACvgZ, + _$s14LinearMediaKit19FullscreenBehaviorsV21sceneSizeRestrictionsACvgZ, _$s14LinearMediaKit19FullscreenBehaviorsV7defaultSayACGvgZ, _$s14LinearMediaKit19FullscreenBehaviorsV8rawValueACSi_tcfC, _$s14LinearMediaKit19FullscreenBehaviorsV8rawValueSivg, _$s14LinearMediaKit19FullscreenBehaviorsVMa, _$s14LinearMediaKit19FullscreenBehaviorsVMn, _$s14LinearMediaKit22PlayableViewControllerC023environmentPickerButtoneF0So06UIViewF0CSgvgTj, _$s14LinearMediaKit22PlayableViewControllerC18prefersAutoDimmingSbvsTj, From f5a697f57dea2175017d407e06f4c585e58aa237 Mon Sep 17 00:00:00 2001 From: Alex Christensen Date: Fri, 21 Feb 2025 16:45:31 -0800 Subject: [PATCH 017/174] Move WebFullScreenManagerProxy::willEnterFullScreen to a lambda https://bugs.webkit.org/show_bug.cgi?id=288220 rdar://145312937 Reviewed by Charlie Wolfe. There's no need to find the WebFullScreenManagerProxy to give it the CompletionHandler. Just call the CompletionHandler, and it has the context needed to do what it needs to do. Also, the NSScreen given to WKFullScreenWindowController was only nil, so remove that parameter. And make the signature of enterFullScreen no longer platform-dependent. No change in behavior, this just makes the code more sane. * Source/WebKit/UIProcess/API/C/WKPage.cpp: (WKPageSetFullScreenClientForTesting): * Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp: (WebKit::PageClientImpl::enterFullScreen): * Source/WebKit/UIProcess/API/gtk/PageClientImpl.h: * Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp: (webkitWebViewBaseWillEnterFullScreen): * Source/WebKit/UIProcess/API/wpe/PageClientImpl.h: * Source/WebKit/UIProcess/API/wpe/WPEWebView.cpp: (WKWPE::View::willEnterFullScreen): * Source/WebKit/UIProcess/WebFullScreenManagerProxy.cpp: (WebKit::WebFullScreenManagerProxy::enterFullScreen): (WebKit::WebFullScreenManagerProxy::willEnterFullScreen): Deleted. * Source/WebKit/UIProcess/WebFullScreenManagerProxy.h: (WebKit::WebFullScreenManagerProxy::fullscreenState const): * Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm: (-[WKFullScreenWindowController _enterFullScreen:windowScene:completionHandler:]): * Source/WebKit/UIProcess/mac/PageClientImplMac.h: * Source/WebKit/UIProcess/mac/PageClientImplMac.mm: (WebKit::PageClientImpl::enterFullScreen): * Source/WebKit/UIProcess/mac/WKFullScreenWindowController.h: * Source/WebKit/UIProcess/mac/WKFullScreenWindowController.mm: (-[WKFullScreenWindowController enterFullScreen:]): (-[WKFullScreenWindowController enterFullScreen:completionHandler:]): Deleted. * Source/WebKit/UIProcess/playstation/PageClientImpl.cpp: (WebKit::PageClientImpl::enterFullScreen): * Source/WebKit/UIProcess/playstation/PageClientImpl.h: * Source/WebKit/UIProcess/playstation/PlayStationWebView.cpp: (WebKit::PlayStationWebView::willEnterFullScreen): * Source/WebKit/UIProcess/win/PageClientImpl.cpp: (WebKit::PageClientImpl::enterFullScreen): * Source/WebKit/UIProcess/win/PageClientImpl.h: Canonical link: https://commits.webkit.org/290838@main --- Source/WebKit/UIProcess/API/C/WKPage.cpp | 6 +-- .../UIProcess/API/gtk/PageClientImpl.cpp | 2 +- .../WebKit/UIProcess/API/gtk/PageClientImpl.h | 2 +- .../UIProcess/API/gtk/WebKitWebViewBase.cpp | 5 +-- .../UIProcess/API/wpe/PageClientImpl.cpp | 2 +- .../WebKit/UIProcess/API/wpe/PageClientImpl.h | 2 +- .../WebKit/UIProcess/API/wpe/WPEWebView.cpp | 5 +-- .../UIProcess/WebFullScreenManagerProxy.cpp | 39 +++++++------------ .../UIProcess/WebFullScreenManagerProxy.h | 5 --- .../WKFullScreenWindowControllerIOS.mm | 26 +++---------- .../WebKit/UIProcess/mac/PageClientImplMac.h | 2 +- .../WebKit/UIProcess/mac/PageClientImplMac.mm | 4 +- .../mac/WKFullScreenWindowController.h | 2 +- .../mac/WKFullScreenWindowController.mm | 7 ++-- .../UIProcess/playstation/PageClientImpl.cpp | 2 +- .../UIProcess/playstation/PageClientImpl.h | 2 +- .../playstation/PlayStationWebView.cpp | 2 +- .../WebKit/UIProcess/win/PageClientImpl.cpp | 2 +- Source/WebKit/UIProcess/win/PageClientImpl.h | 2 +- 19 files changed, 40 insertions(+), 79 deletions(-) diff --git a/Source/WebKit/UIProcess/API/C/WKPage.cpp b/Source/WebKit/UIProcess/API/C/WKPage.cpp index 6034bcba893b7..261d3064b6cdc 100644 --- a/Source/WebKit/UIProcess/API/C/WKPage.cpp +++ b/Source/WebKit/UIProcess/API/C/WKPage.cpp @@ -1212,11 +1212,7 @@ void WKPageSetFullScreenClientForTesting(WKPageRef pageRef, const WKPageFullScre void closeFullScreenManager() override { } bool isFullScreen() override { return false; } - void enterFullScreen( -#if PLATFORM(IOS_FAMILY) - WebCore::FloatSize, -#endif - CompletionHandler&& completionHandler) override + void enterFullScreen(WebCore::FloatSize, CompletionHandler&& completionHandler) override { if (!m_client.willEnterFullScreen) return completionHandler(false); diff --git a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp index 74960c796233e..986edbe2d0b6f 100644 --- a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp +++ b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp @@ -413,7 +413,7 @@ bool PageClientImpl::isFullScreen() return webkitWebViewBaseIsFullScreen(WEBKIT_WEB_VIEW_BASE(m_viewWidget)); } -void PageClientImpl::enterFullScreen(CompletionHandler&& completionHandler) +void PageClientImpl::enterFullScreen(WebCore::FloatSize, CompletionHandler&& completionHandler) { if (!m_viewWidget) return completionHandler(false); diff --git a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h index 901667c226f59..bd11269e156a9 100644 --- a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h +++ b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h @@ -132,7 +132,7 @@ class PageClientImpl : public PageClient // WebFullScreenManagerProxyClient void closeFullScreenManager() override; bool isFullScreen() override; - void enterFullScreen(CompletionHandler&&) override; + void enterFullScreen(WebCore::FloatSize, CompletionHandler&&) override; void exitFullScreen(CompletionHandler&&) override; void beganEnterFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; void beganExitFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp index aad41d0c83232..597dcdbd94e88 100644 --- a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp +++ b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp @@ -2624,10 +2624,7 @@ void webkitWebViewBaseWillEnterFullScreen(WebKitWebViewBase* webkitWebViewBase, { WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv; ASSERT(priv->fullScreenState == WebFullScreenManagerProxy::FullscreenState::NotInFullscreen); - if (auto* fullScreenManagerProxy = priv->pageProxy->fullScreenManager()) - fullScreenManagerProxy->willEnterFullScreen(WTFMove(completionHandler)); - else - completionHandler(false); + completionHandler(true); priv->fullScreenState = WebFullScreenManagerProxy::FullscreenState::EnteringFullscreen; } diff --git a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp index bb48ef101f68b..76f77a9a50f72 100644 --- a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp +++ b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp @@ -434,7 +434,7 @@ bool PageClientImpl::isFullScreen() return m_view.isFullScreen(); } -void PageClientImpl::enterFullScreen(CompletionHandler&& completionHandler) +void PageClientImpl::enterFullScreen(WebCore::FloatSize, CompletionHandler&& completionHandler) { if (isFullScreen()) return completionHandler(false); diff --git a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h index f0de853a118e1..c9b5db9dc3005 100644 --- a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h +++ b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h @@ -167,7 +167,7 @@ class PageClientImpl final : public PageClient void closeFullScreenManager() override; bool isFullScreen() override; - void enterFullScreen(CompletionHandler&&) override; + void enterFullScreen(WebCore::FloatSize, CompletionHandler&&) override; void exitFullScreen(CompletionHandler&&) override; void beganEnterFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; void beganExitFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; diff --git a/Source/WebKit/UIProcess/API/wpe/WPEWebView.cpp b/Source/WebKit/UIProcess/API/wpe/WPEWebView.cpp index 3e4f8087f77bf..6d6076e877be6 100644 --- a/Source/WebKit/UIProcess/API/wpe/WPEWebView.cpp +++ b/Source/WebKit/UIProcess/API/wpe/WPEWebView.cpp @@ -153,10 +153,7 @@ bool View::isFullScreen() const void View::willEnterFullScreen(CompletionHandler&& completionHandler) { ASSERT(m_fullscreenState == WebFullScreenManagerProxy::FullscreenState::NotInFullscreen); - if (auto* fullScreenManagerProxy = page().fullScreenManager()) - fullScreenManagerProxy->willEnterFullScreen(WTFMove(completionHandler)); - else - completionHandler(false); + completionHandler(true); m_fullscreenState = WebFullScreenManagerProxy::FullscreenState::EnteringFullscreen; } diff --git a/Source/WebKit/UIProcess/WebFullScreenManagerProxy.cpp b/Source/WebKit/UIProcess/WebFullScreenManagerProxy.cpp index 48d09620d4342..ab744e5a4672f 100644 --- a/Source/WebKit/UIProcess/WebFullScreenManagerProxy.cpp +++ b/Source/WebKit/UIProcess/WebFullScreenManagerProxy.cpp @@ -88,16 +88,6 @@ std::optional WebFullScreenManagerProxy::sharedP return dynamicDowncast(AuxiliaryProcessProxy::fromConnection(connection))->sharedPreferencesForWebProcess(); } -void WebFullScreenManagerProxy::willEnterFullScreen(CompletionHandler&& completionHandler) -{ - ALWAYS_LOG(LOGIDENTIFIER); - m_fullscreenState = FullscreenState::EnteringFullscreen; - - if (RefPtr page = m_page.get()) - page->fullscreenClient().willEnterFullscreen(page.get()); - completionHandler(true); -} - template void WebFullScreenManagerProxy::sendToWebProcess(M&& message) { RefPtr page = m_page.get(); @@ -204,7 +194,6 @@ void WebFullScreenManagerProxy::enterFullScreen(IPC::Connection& connection, boo m_fullScreenProcess = dynamicDowncast(AuxiliaryProcessProxy::fromConnection(connection)); m_blocksReturnToFullscreenFromPictureInPicture = blocksReturnToFullscreenFromPictureInPicture; #if PLATFORM(IOS_FAMILY) - #if ENABLE(VIDEO_USES_ELEMENT_FULLSCREEN) m_isVideoElement = mediaDetails.type == FullScreenMediaDetails::Type::Video; #endif @@ -215,20 +204,22 @@ void WebFullScreenManagerProxy::enterFullScreen(IPC::Connection& connection, boo m_imageBuffer = sharedMemoryBuffer->createSharedBuffer(sharedMemoryBuffer->size()); } m_imageMIMEType = mediaDetails.mimeType; -#endif // QUICKLOOK_FULLSCREEN +#endif // ENABLE(QUICKLOOK_FULLSCREEN) +#endif // PLATFORM(IOS_FAMILY) - auto mediaDimensions = mediaDetails.mediaDimensions; - if (CheckedPtr client = m_client) - client->enterFullScreen(mediaDimensions, WTFMove(completionHandler)); - else - completionHandler(false); -#else - UNUSED_PARAM(mediaDetails); - if (CheckedPtr client = m_client) - client->enterFullScreen(WTFMove(completionHandler)); - else - completionHandler(false); -#endif + CheckedPtr client = m_client; + if (!client) + return completionHandler(false); + + client->enterFullScreen(mediaDetails.mediaDimensions, [this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler), logIdentifier = LOGIDENTIFIER] (bool success) mutable { + ALWAYS_LOG(logIdentifier); + if (success) { + m_fullscreenState = FullscreenState::EnteringFullscreen; + if (RefPtr page = m_page.get()) + page->fullscreenClient().willEnterFullscreen(page.get()); + } + completionHandler(success); + }); } #if ENABLE(QUICKLOOK_FULLSCREEN) diff --git a/Source/WebKit/UIProcess/WebFullScreenManagerProxy.h b/Source/WebKit/UIProcess/WebFullScreenManagerProxy.h index b31d0e979a419..fa87e06845728 100644 --- a/Source/WebKit/UIProcess/WebFullScreenManagerProxy.h +++ b/Source/WebKit/UIProcess/WebFullScreenManagerProxy.h @@ -64,11 +64,7 @@ class WebFullScreenManagerProxyClient : public CanMakeCheckedPtr&&) = 0; -#else - virtual void enterFullScreen(CompletionHandler&&) = 0; -#endif #if ENABLE(QUICKLOOK_FULLSCREEN) virtual void updateImageSource() = 0; #endif @@ -112,7 +108,6 @@ class WebFullScreenManagerProxy : public IPC::MessageReceiver, public CanMakeChe ExitingFullscreen, }; FullscreenState fullscreenState() const { return m_fullscreenState; } - void willEnterFullScreen(CompletionHandler&&); void setAnimatingFullScreen(bool); void requestRestoreFullScreen(CompletionHandler&&); void requestExitFullScreen(); diff --git a/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm b/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm index b7793f702cd19..d079e865fc060 100644 --- a/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm +++ b/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm @@ -940,7 +940,7 @@ - (void)_enterFullScreen:(CGSize)mediaDimensions windowScene:(UIWindowScene *)wi _fullScreenState = WebKit::WaitingToEnterFullScreen; OBJC_ALWAYS_LOG(OBJC_LOGIDENTIFIER, "(QL) presentation updated"); - manager->willEnterFullScreen(WTFMove(completionHandler)); + completionHandler(true); manager->prepareQuickLookImageURL([strongSelf = retainPtr(self), self, window = retainPtr([webView window]), completionHandler = WTFMove(completionHandler), logIdentifier = OBJC_LOGIDENTIFIER] (URL&& url) mutable { UIWindowScene *scene = [window windowScene]; @@ -949,14 +949,8 @@ - (void)_enterFullScreen:(CGSize)mediaDimensions windowScene:(UIWindowScene *)wi [_previewWindowController presentWindow]; _fullScreenState = WebKit::InFullScreen; - if (auto* manager = [strongSelf _manager]) { - OBJC_ALWAYS_LOG(logIdentifier, "(QL) presentation completed"); - return completionHandler(true); - } - - OBJC_ERROR_LOG(logIdentifier, "(QL) presentation completed, but manager missing"); - [strongSelf _exitFullscreenImmediately]; - ASSERT_NOT_REACHED(); + OBJC_ALWAYS_LOG(logIdentifier, "(QL) presentation completed"); + return completionHandler(true); }); return; } @@ -1098,17 +1092,9 @@ - (void)_enterFullScreen:(CGSize)mediaDimensions windowScene:(UIWindowScene *)wi } [self._webView _doAfterNextVisibleContentRectAndPresentationUpdate:makeBlockPtr([self, protectedSelf, logIdentifier, completionHandler = WTFMove(completionHandler)] mutable { - if (auto* manager = [protectedSelf _manager]) { - OBJC_ALWAYS_LOG(logIdentifier, "presentation updated"); - WebKit::WKWebViewState().applyTo(self._webView); - manager->willEnterFullScreen(WTFMove(completionHandler)); - return; - } - - ASSERT_NOT_REACHED(); - OBJC_ERROR_LOG(logIdentifier, "presentation updated, but manager missing"); - [protectedSelf _exitFullscreenImmediately]; - completionHandler(false); + OBJC_ALWAYS_LOG(logIdentifier, "presentation updated"); + WebKit::WKWebViewState().applyTo(self._webView); + return completionHandler(true); }).get()]; }); diff --git a/Source/WebKit/UIProcess/mac/PageClientImplMac.h b/Source/WebKit/UIProcess/mac/PageClientImplMac.h index 0b09f15b4ff1e..51744982af6f5 100644 --- a/Source/WebKit/UIProcess/mac/PageClientImplMac.h +++ b/Source/WebKit/UIProcess/mac/PageClientImplMac.h @@ -222,7 +222,7 @@ class PageClientImpl final : public PageClientImplCocoa // WebFullScreenManagerProxyClient void closeFullScreenManager() override; bool isFullScreen() override; - void enterFullScreen(CompletionHandler&&) override; + void enterFullScreen(WebCore::FloatSize, CompletionHandler&&) override; void exitFullScreen(CompletionHandler&&) override; void beganEnterFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; void beganExitFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; diff --git a/Source/WebKit/UIProcess/mac/PageClientImplMac.mm b/Source/WebKit/UIProcess/mac/PageClientImplMac.mm index 0c3012967fef6..6f5fd6287d435 100644 --- a/Source/WebKit/UIProcess/mac/PageClientImplMac.mm +++ b/Source/WebKit/UIProcess/mac/PageClientImplMac.mm @@ -824,11 +824,11 @@ static inline NSCorrectionResponse toCorrectionResponse(AutocorrectionResponse r return m_impl->fullScreenWindowController().isFullScreen; } -void PageClientImpl::enterFullScreen(CompletionHandler&& completionHandler) +void PageClientImpl::enterFullScreen(FloatSize, CompletionHandler&& completionHandler) { if (!m_impl->fullScreenWindowController()) return completionHandler(false); - [m_impl->fullScreenWindowController() enterFullScreen:nil completionHandler:WTFMove(completionHandler)]; + [m_impl->fullScreenWindowController() enterFullScreen:WTFMove(completionHandler)]; } void PageClientImpl::exitFullScreen(CompletionHandler&& completionHandler) diff --git a/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.h b/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.h index fb706b86b55fe..b6da54f5dcdc8 100644 --- a/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.h +++ b/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.h @@ -77,7 +77,7 @@ typedef enum FullScreenState : NSInteger FullScreenState; - (BOOL)isFullScreen; -- (void)enterFullScreen:(NSScreen *)screen completionHandler:(CompletionHandler&&)completionHandler; +- (void)enterFullScreen:(CompletionHandler&&)completionHandler; - (void)exitFullScreen:(CompletionHandler&&)completionHandler; - (void)exitFullScreenImmediately; - (void)requestExitFullScreen; diff --git a/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.mm b/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.mm index 596a85466b174..ff45b52851a67 100644 --- a/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.mm +++ b/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.mm @@ -227,14 +227,13 @@ - (void)noResponderFor:(SEL)eventMethod return adoptCF(CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, provider.get(), 0, shouldInterpolate, intent)); } -- (void)enterFullScreen:(NSScreen *)screen completionHandler:(CompletionHandler&&)completionHandler +- (void)enterFullScreen:(CompletionHandler&&)completionHandler { if ([self isFullScreen]) return completionHandler(false); _fullScreenState = WaitingToEnterFullScreen; - if (!screen) - screen = [NSScreen mainScreen]; + NSScreen *screen = [NSScreen mainScreen]; NSRect screenFrame = WebCore::safeScreenFrame(screen); NSRect webViewFrame = convertRectToScreen([_webView window], [_webView convertRect:[_webView frame] toView:nil]); @@ -283,7 +282,7 @@ - (void)enterFullScreen:(NSScreen *)screen completionHandler:(CompletionHandler< _savedScale = _page->pageScaleFactor(); _page->scalePageRelativeToScrollPosition(1, { }); [self _manager]->setAnimatingFullScreen(true); - [self _manager]->willEnterFullScreen(WTFMove(completionHandler)); + completionHandler(true); } - (void)beganEnterFullScreenWithInitialFrame:(NSRect)initialFrame finalFrame:(NSRect)finalFrame completionHandler:(CompletionHandler&&)completionHandler diff --git a/Source/WebKit/UIProcess/playstation/PageClientImpl.cpp b/Source/WebKit/UIProcess/playstation/PageClientImpl.cpp index 8c24312e443cf..df5116147712b 100644 --- a/Source/WebKit/UIProcess/playstation/PageClientImpl.cpp +++ b/Source/WebKit/UIProcess/playstation/PageClientImpl.cpp @@ -261,7 +261,7 @@ bool PageClientImpl::isFullScreen() return m_view.isFullScreen(); } -void PageClientImpl::enterFullScreen(CompletionHandler&& completionHandler) +void PageClientImpl::enterFullScreen(WebCore::FloatSize, CompletionHandler&& completionHandler) { m_view.enterFullScreen(WTFMove(completionHandler)); } diff --git a/Source/WebKit/UIProcess/playstation/PageClientImpl.h b/Source/WebKit/UIProcess/playstation/PageClientImpl.h index e2f74554da80b..066da7e2bf276 100644 --- a/Source/WebKit/UIProcess/playstation/PageClientImpl.h +++ b/Source/WebKit/UIProcess/playstation/PageClientImpl.h @@ -137,7 +137,7 @@ class PageClientImpl final : public PageClient void closeFullScreenManager() override; bool isFullScreen() override; - void enterFullScreen(CompletionHandler&&) override; + void enterFullScreen(WebCore::FloatSize, CompletionHandler&&) override; void exitFullScreen(CompletionHandler&&) override; void beganEnterFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; void beganExitFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; diff --git a/Source/WebKit/UIProcess/playstation/PlayStationWebView.cpp b/Source/WebKit/UIProcess/playstation/PlayStationWebView.cpp index 27001b6163ef6..97251bf8d467f 100644 --- a/Source/WebKit/UIProcess/playstation/PlayStationWebView.cpp +++ b/Source/WebKit/UIProcess/playstation/PlayStationWebView.cpp @@ -118,7 +118,7 @@ void PlayStationWebView::setViewNeedsDisplay(const WebCore::Region& region) void PlayStationWebView::willEnterFullScreen(CompletionHandler&& completionHandler) { m_isFullScreen = true; - m_page->fullScreenManager()->willEnterFullScreen(WTFMove(completionHandler)); + completionHandler(true); } void PlayStationWebView::requestExitFullScreen() diff --git a/Source/WebKit/UIProcess/win/PageClientImpl.cpp b/Source/WebKit/UIProcess/win/PageClientImpl.cpp index 52fb0172a1215..afebb478e850d 100644 --- a/Source/WebKit/UIProcess/win/PageClientImpl.cpp +++ b/Source/WebKit/UIProcess/win/PageClientImpl.cpp @@ -318,7 +318,7 @@ bool PageClientImpl::isFullScreen() return false; } -void PageClientImpl::enterFullScreen(CompletionHandler&& completionHandler) +void PageClientImpl::enterFullScreen(FloatSize, CompletionHandler&& completionHandler) { notImplemented(); completionHandler(false); diff --git a/Source/WebKit/UIProcess/win/PageClientImpl.h b/Source/WebKit/UIProcess/win/PageClientImpl.h index 7e03c1c5d04b3..bd1984fbf3c37 100644 --- a/Source/WebKit/UIProcess/win/PageClientImpl.h +++ b/Source/WebKit/UIProcess/win/PageClientImpl.h @@ -127,7 +127,7 @@ class PageClientImpl : public PageClient // WebFullScreenManagerProxyClient void closeFullScreenManager() override; bool isFullScreen() override; - void enterFullScreen(CompletionHandler&&) override; + void enterFullScreen(WebCore::FloatSize, CompletionHandler&&) override; void exitFullScreen(CompletionHandler&&) override; void beganEnterFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; void beganExitFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; From 9996e4031b794c1c166ff57020304d56e55291f7 Mon Sep 17 00:00:00 2001 From: Yusuke Suzuki Date: Fri, 21 Feb 2025 16:51:00 -0800 Subject: [PATCH 018/174] [JSC] DirectArguments::visitChildrenImpl calls JSObject::visitChildrenImpl twice https://bugs.webkit.org/show_bug.cgi?id=287502 rdar://145080419 Reviewed by Yijia Huang. Avoid calling Base::visitChildren twice. We also rename GenericArguments to GenericArgumentsImpl to make it clear that this is Impl interface for JSCells. * Source/JavaScriptCore/CMakeLists.txt: * Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj: * Source/JavaScriptCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations: * Source/JavaScriptCore/runtime/DirectArguments.cpp: (JSC::DirectArguments::DirectArguments): (JSC::DirectArguments::visitChildrenImpl): (JSC::DirectArguments::copyToArguments): * Source/JavaScriptCore/runtime/DirectArguments.h: * Source/JavaScriptCore/runtime/GenericArgumentsImpl.h: Renamed from Source/JavaScriptCore/runtime/GenericArguments.h. (JSC::GenericArgumentsImpl::GenericArgumentsImpl): * Source/JavaScriptCore/runtime/GenericArgumentsImplInlines.h: Renamed from Source/JavaScriptCore/runtime/GenericArgumentsInlines.h. (JSC::GenericArgumentsImpl::visitChildrenImpl): (JSC::GenericArgumentsImpl::getOwnPropertySlot): (JSC::GenericArgumentsImpl::getOwnPropertySlotByIndex): (JSC::GenericArgumentsImpl::getOwnPropertyNames): (JSC::GenericArgumentsImpl::put): (JSC::GenericArgumentsImpl::putByIndex): (JSC::GenericArgumentsImpl::deleteProperty): (JSC::GenericArgumentsImpl::deletePropertyByIndex): (JSC::GenericArgumentsImpl::defineOwnProperty): (JSC::GenericArgumentsImpl::initModifiedArgumentsDescriptor): (JSC::GenericArgumentsImpl::initModifiedArgumentsDescriptorIfNecessary): (JSC::GenericArgumentsImpl::setModifiedArgumentDescriptor): (JSC::GenericArgumentsImpl::isModifiedArgumentDescriptor): (JSC::GenericArgumentsImpl::copyToArguments): * Source/JavaScriptCore/runtime/ScopedArguments.cpp: (JSC::ScopedArguments::ScopedArguments): (JSC::ScopedArguments::visitChildrenImpl): (JSC::ScopedArguments::copyToArguments): * Source/JavaScriptCore/runtime/ScopedArguments.h: Canonical link: https://commits.webkit.org/290839@main --- Source/JavaScriptCore/CMakeLists.txt | 2 +- .../JavaScriptCore.xcodeproj/project.pbxproj | 16 ++--- .../MemoryUnsafeCastCheckerExpectations | 2 +- .../runtime/DirectArguments.cpp | 9 ++- .../JavaScriptCore/runtime/DirectArguments.h | 12 ++-- ...ericArguments.h => GenericArgumentsImpl.h} | 8 +-- ...nlines.h => GenericArgumentsImplInlines.h} | 64 +++++++++---------- .../runtime/ScopedArguments.cpp | 8 +-- .../JavaScriptCore/runtime/ScopedArguments.h | 12 ++-- 9 files changed, 66 insertions(+), 67 deletions(-) rename Source/JavaScriptCore/runtime/{GenericArguments.h => GenericArgumentsImpl.h} (95%) rename Source/JavaScriptCore/runtime/{GenericArgumentsInlines.h => GenericArgumentsImplInlines.h} (83%) diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt index 1439537bc2a16..b52d8892843f6 100644 --- a/Source/JavaScriptCore/CMakeLists.txt +++ b/Source/JavaScriptCore/CMakeLists.txt @@ -1019,7 +1019,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS runtime/FunctionRareData.h runtime/FuzzerAgent.h runtime/Gate.h - runtime/GenericArguments.h + runtime/GenericArgumentsImpl.h runtime/GenericOffset.h runtime/GenericTypedArrayView.h runtime/GenericTypedArrayViewInlines.h diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj index 1f3bdecf12a1d..bf871fddabd99 100644 --- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj +++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj @@ -550,8 +550,8 @@ 0FE050141AA9091100D33B33 /* ArgumentsMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE0500C1AA9091100D33B33 /* ArgumentsMode.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FE050161AA9091100D33B33 /* DirectArgumentsOffset.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE0500E1AA9091100D33B33 /* DirectArgumentsOffset.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FE050181AA9091100D33B33 /* DirectArguments.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE050101AA9091100D33B33 /* DirectArguments.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 0FE050191AA9091100D33B33 /* GenericArguments.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE050111AA9091100D33B33 /* GenericArguments.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 0FE0501A1AA9091100D33B33 /* GenericArgumentsInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE050121AA9091100D33B33 /* GenericArgumentsInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0FE050191AA9091100D33B33 /* GenericArgumentsImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE050111AA9091100D33B33 /* GenericArgumentsImpl.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0FE0501A1AA9091100D33B33 /* GenericArgumentsImplInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE050121AA9091100D33B33 /* GenericArgumentsImplInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FE0501B1AA9091100D33B33 /* GenericOffset.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE050131AA9091100D33B33 /* GenericOffset.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FE050261AA9095600D33B33 /* ClonedArguments.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE0501D1AA9095600D33B33 /* ClonedArguments.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FE050281AA9095600D33B33 /* ScopedArguments.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE0501F1AA9095600D33B33 /* ScopedArguments.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -3387,8 +3387,8 @@ 0FE0500E1AA9091100D33B33 /* DirectArgumentsOffset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectArgumentsOffset.h; sourceTree = ""; }; 0FE0500F1AA9091100D33B33 /* DirectArguments.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DirectArguments.cpp; sourceTree = ""; }; 0FE050101AA9091100D33B33 /* DirectArguments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectArguments.h; sourceTree = ""; }; - 0FE050111AA9091100D33B33 /* GenericArguments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenericArguments.h; sourceTree = ""; }; - 0FE050121AA9091100D33B33 /* GenericArgumentsInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenericArgumentsInlines.h; sourceTree = ""; }; + 0FE050111AA9091100D33B33 /* GenericArgumentsImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenericArgumentsImpl.h; sourceTree = ""; }; + 0FE050121AA9091100D33B33 /* GenericArgumentsImplInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenericArgumentsImplInlines.h; sourceTree = ""; }; 0FE050131AA9091100D33B33 /* GenericOffset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenericOffset.h; sourceTree = ""; }; 0FE0501C1AA9095600D33B33 /* ClonedArguments.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClonedArguments.cpp; sourceTree = ""; }; 0FE0501D1AA9095600D33B33 /* ClonedArguments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClonedArguments.h; sourceTree = ""; }; @@ -8278,8 +8278,8 @@ 70B791891C024432002481E2 /* GeneratorPrototype.cpp */, 70B7918A1C024432002481E2 /* GeneratorPrototype.h */, 276B385D2A71D0B600252F4E /* GeneratorPrototypeInlines.h */, - 0FE050111AA9091100D33B33 /* GenericArguments.h */, - 0FE050121AA9091100D33B33 /* GenericArgumentsInlines.h */, + 0FE050111AA9091100D33B33 /* GenericArgumentsImpl.h */, + 0FE050121AA9091100D33B33 /* GenericArgumentsImplInlines.h */, 0FE050131AA9091100D33B33 /* GenericOffset.h */, 0F2B66B217B6B5AB00A7AE3F /* GenericTypedArrayView.h */, 0F2B66B317B6B5AB00A7AE3F /* GenericTypedArrayViewInlines.h */, @@ -11108,8 +11108,8 @@ 276B386B2A71D0B600252F4E /* GeneratorFunctionPrototypeInlines.h in Headers */, 70B791991C024A29002481E2 /* GeneratorPrototype.h in Headers */, 276B386C2A71D0B600252F4E /* GeneratorPrototypeInlines.h in Headers */, - 0FE050191AA9091100D33B33 /* GenericArguments.h in Headers */, - 0FE0501A1AA9091100D33B33 /* GenericArgumentsInlines.h in Headers */, + 0FE050191AA9091100D33B33 /* GenericArgumentsImpl.h in Headers */, + 0FE0501A1AA9091100D33B33 /* GenericArgumentsImplInlines.h in Headers */, 0FE0501B1AA9091100D33B33 /* GenericOffset.h in Headers */, 0F2B66E017B6B5AB00A7AE3F /* GenericTypedArrayView.h in Headers */, 0F2B66E117B6B5AB00A7AE3F /* GenericTypedArrayViewInlines.h in Headers */, diff --git a/Source/JavaScriptCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations b/Source/JavaScriptCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations index 530eb85ff914d..dd9f2a3cb9335 100644 --- a/Source/JavaScriptCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations +++ b/Source/JavaScriptCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations @@ -69,7 +69,7 @@ runtime/Exception.cpp runtime/ExecutableBase.cpp runtime/FunctionExecutable.cpp runtime/FunctionRareData.cpp -runtime/GenericArgumentsInlines.h +runtime/GenericArgumentsImplInlines.h runtime/Identifier.cpp runtime/Identifier.h runtime/IdentifierInlines.h diff --git a/Source/JavaScriptCore/runtime/DirectArguments.cpp b/Source/JavaScriptCore/runtime/DirectArguments.cpp index ca321cdfedf8a..98968129ebf0d 100644 --- a/Source/JavaScriptCore/runtime/DirectArguments.cpp +++ b/Source/JavaScriptCore/runtime/DirectArguments.cpp @@ -27,7 +27,7 @@ #include "DirectArguments.h" #include "CodeBlock.h" -#include "GenericArgumentsInlines.h" +#include "GenericArgumentsImplInlines.h" WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN @@ -38,7 +38,7 @@ STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(DirectArguments); const ClassInfo DirectArguments::s_info = { "Arguments"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(DirectArguments) }; DirectArguments::DirectArguments(VM& vm, Structure* structure, unsigned length, unsigned capacity) - : GenericArguments(vm, structure) + : GenericArgumentsImpl(vm, structure) , m_length(length) , m_minCapacity(capacity) { @@ -97,14 +97,13 @@ void DirectArguments::visitChildrenImpl(JSCell* thisCell, Visitor& visitor) { DirectArguments* thisObject = static_cast(thisCell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); - Base::visitChildren(thisObject, visitor); + GenericArgumentsImpl::visitChildren(thisCell, visitor); // Including Base::visitChildren. visitor.appendValues(thisObject->storage(), std::max(thisObject->m_length, thisObject->m_minCapacity)); visitor.append(thisObject->m_callee); if (thisObject->m_mappedArguments) visitor.markAuxiliary(thisObject->m_mappedArguments.get()); - GenericArguments::visitChildren(thisCell, visitor); } DEFINE_VISIT_CHILDREN(DirectArguments); @@ -165,7 +164,7 @@ void DirectArguments::copyToArguments(JSGlobalObject* globalObject, JSValue* fir return; } - GenericArguments::copyToArguments(globalObject, firstElementDest, offset, length); + GenericArgumentsImpl::copyToArguments(globalObject, firstElementDest, offset, length); } unsigned DirectArguments::mappedArgumentsSize() diff --git a/Source/JavaScriptCore/runtime/DirectArguments.h b/Source/JavaScriptCore/runtime/DirectArguments.h index 783f7a07e9f76..c5d1e36bfb897 100644 --- a/Source/JavaScriptCore/runtime/DirectArguments.h +++ b/Source/JavaScriptCore/runtime/DirectArguments.h @@ -27,7 +27,7 @@ #include "CagedBarrierPtr.h" #include "DirectArgumentsOffset.h" -#include "GenericArguments.h" +#include "GenericArgumentsImpl.h" #include WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN @@ -43,7 +43,7 @@ namespace JSC { // // To speed allocation, this object will hold all of the arguments in-place. The arguments as well // as a table of flags saying which arguments were overridden. -class DirectArguments final : public GenericArguments { +class DirectArguments final : public GenericArgumentsImpl { private: DirectArguments(VM&, Structure*, unsigned length, unsigned capacity); @@ -125,7 +125,7 @@ class DirectArguments final : public GenericArguments { return storage()[offset.offset()]; } - // Methods intended for use by the GenericArguments mixin. + // Methods intended for use by the GenericArgumentsImpl mixin. bool overrodeThings() const { return !!m_mappedArguments; } void overrideThings(JSGlobalObject*); void overrideThingsIfNecessary(JSGlobalObject*); @@ -133,17 +133,17 @@ class DirectArguments final : public GenericArguments { void initModifiedArgumentsDescriptorIfNecessary(JSGlobalObject* globalObject) { - GenericArguments::initModifiedArgumentsDescriptorIfNecessary(globalObject, m_length); + GenericArgumentsImpl::initModifiedArgumentsDescriptorIfNecessary(globalObject, m_length); } void setModifiedArgumentDescriptor(JSGlobalObject* globalObject, unsigned index) { - GenericArguments::setModifiedArgumentDescriptor(globalObject, index, m_length); + GenericArgumentsImpl::setModifiedArgumentDescriptor(globalObject, index, m_length); } bool isModifiedArgumentDescriptor(unsigned index) { - return GenericArguments::isModifiedArgumentDescriptor(index, m_length); + return GenericArgumentsImpl::isModifiedArgumentDescriptor(index, m_length); } void copyToArguments(JSGlobalObject*, JSValue* firstElementDest, unsigned offset, unsigned length); diff --git a/Source/JavaScriptCore/runtime/GenericArguments.h b/Source/JavaScriptCore/runtime/GenericArgumentsImpl.h similarity index 95% rename from Source/JavaScriptCore/runtime/GenericArguments.h rename to Source/JavaScriptCore/runtime/GenericArgumentsImpl.h index 731cdb7003ef1..975d52bd89f41 100644 --- a/Source/JavaScriptCore/runtime/GenericArguments.h +++ b/Source/JavaScriptCore/runtime/GenericArgumentsImpl.h @@ -33,13 +33,13 @@ namespace JSC { // This is a mixin for the two kinds of Arguments-class objects that arise when you say // "arguments" inside a function. This class doesn't show up in the JSCell inheritance hierarchy. template -class GenericArguments : public JSNonFinalObject { +class GenericArgumentsImpl : public JSNonFinalObject { public: - typedef JSNonFinalObject Base; + using Base = JSNonFinalObject; static constexpr unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetOwnPropertyNames | OverridesPut | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero; protected: - GenericArguments(VM& vm, Structure* structure) + GenericArgumentsImpl(VM& vm, Structure* structure) : Base(vm, structure) { } @@ -53,7 +53,7 @@ class GenericArguments : public JSNonFinalObject { static bool deleteProperty(JSCell*, JSGlobalObject*, PropertyName, DeletePropertySlot&); static bool deletePropertyByIndex(JSCell*, JSGlobalObject*, unsigned propertyName); static bool defineOwnProperty(JSObject*, JSGlobalObject*, PropertyName, const PropertyDescriptor&, bool shouldThrow); - + void initModifiedArgumentsDescriptor(JSGlobalObject*, unsigned length); void initModifiedArgumentsDescriptorIfNecessary(JSGlobalObject*, unsigned length); void setModifiedArgumentDescriptor(JSGlobalObject*, unsigned index, unsigned length); diff --git a/Source/JavaScriptCore/runtime/GenericArgumentsInlines.h b/Source/JavaScriptCore/runtime/GenericArgumentsImplInlines.h similarity index 83% rename from Source/JavaScriptCore/runtime/GenericArgumentsInlines.h rename to Source/JavaScriptCore/runtime/GenericArgumentsImplInlines.h index d7d7675f4f7b4..88da6e5321b78 100644 --- a/Source/JavaScriptCore/runtime/GenericArgumentsInlines.h +++ b/Source/JavaScriptCore/runtime/GenericArgumentsImplInlines.h @@ -25,7 +25,7 @@ #pragma once -#include "GenericArguments.h" +#include "GenericArgumentsImpl.h" #include "JSCInlines.h" WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN @@ -34,24 +34,24 @@ namespace JSC { template template -void GenericArguments::visitChildrenImpl(JSCell* thisCell, Visitor& visitor) +void GenericArgumentsImpl::visitChildrenImpl(JSCell* thisCell, Visitor& visitor) { Type* thisObject = static_cast(thisCell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); Base::visitChildren(thisCell, visitor); - - if (thisObject->m_modifiedArgumentsDescriptor) - visitor.markAuxiliary(thisObject->m_modifiedArgumentsDescriptor.getUnsafe()); + + if (auto* pointer = thisObject->m_modifiedArgumentsDescriptor.getUnsafe()) + visitor.markAuxiliary(pointer); } -DEFINE_VISIT_CHILDREN_WITH_MODIFIER(template, GenericArguments); +DEFINE_VISIT_CHILDREN_WITH_MODIFIER(template, GenericArgumentsImpl); template -bool GenericArguments::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName ident, PropertySlot& slot) +bool GenericArgumentsImpl::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName ident, PropertySlot& slot) { Type* thisObject = jsCast(object); VM& vm = globalObject->vm(); - + if (!thisObject->overrodeThings()) { if (ident == vm.propertyNames->length) { slot.setValue(thisObject, static_cast(PropertyAttribute::DontEnum), jsNumber(thisObject->internalLength())); @@ -66,36 +66,36 @@ bool GenericArguments::getOwnPropertySlot(JSObject* object, JSGlobalObject return true; } } - + if (std::optional index = parseIndex(ident)) - return GenericArguments::getOwnPropertySlotByIndex(thisObject, globalObject, *index, slot); + return GenericArgumentsImpl::getOwnPropertySlotByIndex(thisObject, globalObject, *index, slot); return Base::getOwnPropertySlot(thisObject, globalObject, ident, slot); } template -bool GenericArguments::getOwnPropertySlotByIndex(JSObject* object, JSGlobalObject* globalObject, unsigned index, PropertySlot& slot) +bool GenericArgumentsImpl::getOwnPropertySlotByIndex(JSObject* object, JSGlobalObject* globalObject, unsigned index, PropertySlot& slot) { Type* thisObject = jsCast(object); - + if (!thisObject->isModifiedArgumentDescriptor(index) && thisObject->isMappedArgument(index)) { slot.setValue(thisObject, static_cast(PropertyAttribute::None), thisObject->getIndexQuickly(index)); return true; } - + bool result = Base::getOwnPropertySlotByIndex(object, globalObject, index, slot); - + if (thisObject->isMappedArgument(index)) { ASSERT(result); slot.setValue(thisObject, slot.attributes(), thisObject->getIndexQuickly(index)); return true; } - + return result; } template -void GenericArguments::getOwnPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& array, DontEnumPropertiesMode mode) +void GenericArgumentsImpl::getOwnPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& array, DontEnumPropertiesMode mode) { VM& vm = globalObject->vm(); Type* thisObject = jsCast(object); @@ -118,12 +118,12 @@ void GenericArguments::getOwnPropertyNames(JSObject* object, JSGlobalObjec } template -bool GenericArguments::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName ident, JSValue value, PutPropertySlot& slot) +bool GenericArgumentsImpl::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName ident, JSValue value, PutPropertySlot& slot) { Type* thisObject = jsCast(cell); VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - + if (!thisObject->overrodeThings() && (ident == vm.propertyNames->length || ident == vm.propertyNames->callee @@ -138,7 +138,7 @@ bool GenericArguments::put(JSCell* cell, JSGlobalObject* globalObject, Pro // Fall back to the OrdinarySet when the receiver is altered from the thisObject. if (UNLIKELY(slot.thisValue() != thisObject)) RELEASE_AND_RETURN(scope, Base::put(thisObject, globalObject, ident, value, slot)); - + std::optional index = parseIndex(ident); if (index && thisObject->isMappedArgument(index.value())) { thisObject->setIndexQuickly(vm, index.value(), value); @@ -149,7 +149,7 @@ bool GenericArguments::put(JSCell* cell, JSGlobalObject* globalObject, Pro } template -bool GenericArguments::putByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned index, JSValue value, bool shouldThrow) +bool GenericArgumentsImpl::putByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned index, JSValue value, bool shouldThrow) { Type* thisObject = jsCast(cell); VM& vm = globalObject->vm(); @@ -158,17 +158,17 @@ bool GenericArguments::putByIndex(JSCell* cell, JSGlobalObject* globalObje thisObject->setIndexQuickly(vm, index, value); return true; } - + return Base::putByIndex(cell, globalObject, index, value, shouldThrow); } template -bool GenericArguments::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName ident, DeletePropertySlot& slot) +bool GenericArgumentsImpl::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName ident, DeletePropertySlot& slot) { Type* thisObject = jsCast(cell); VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - + if (!thisObject->overrodeThings() && (ident == vm.propertyNames->length || ident == vm.propertyNames->callee @@ -178,13 +178,13 @@ bool GenericArguments::deleteProperty(JSCell* cell, JSGlobalObject* global } if (std::optional index = parseIndex(ident)) - RELEASE_AND_RETURN(scope, GenericArguments::deletePropertyByIndex(thisObject, globalObject, *index)); + RELEASE_AND_RETURN(scope, GenericArgumentsImpl::deletePropertyByIndex(thisObject, globalObject, *index)); RELEASE_AND_RETURN(scope, Base::deleteProperty(thisObject, globalObject, ident, slot)); } template -bool GenericArguments::deletePropertyByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned index) +bool GenericArgumentsImpl::deletePropertyByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned index) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -214,12 +214,12 @@ bool GenericArguments::deletePropertyByIndex(JSCell* cell, JSGlobalObject* // https://tc39.es/ecma262/#sec-arguments-exotic-objects-defineownproperty-p-desc template -bool GenericArguments::defineOwnProperty(JSObject* object, JSGlobalObject* globalObject, PropertyName ident, const PropertyDescriptor& descriptor, bool shouldThrow) +bool GenericArgumentsImpl::defineOwnProperty(JSObject* object, JSGlobalObject* globalObject, PropertyName ident, const PropertyDescriptor& descriptor, bool shouldThrow) { Type* thisObject = jsCast(object); VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - + if (ident == vm.propertyNames->length || ident == vm.propertyNames->callee || ident == vm.propertyNames->iteratorSymbol) { @@ -270,7 +270,7 @@ bool GenericArguments::defineOwnProperty(JSObject* object, JSGlobalObject* } template -void GenericArguments::initModifiedArgumentsDescriptor(JSGlobalObject* globalObject, unsigned argsLength) +void GenericArgumentsImpl::initModifiedArgumentsDescriptor(JSGlobalObject* globalObject, unsigned argsLength) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -291,14 +291,14 @@ void GenericArguments::initModifiedArgumentsDescriptor(JSGlobalObject* glo } template -void GenericArguments::initModifiedArgumentsDescriptorIfNecessary(JSGlobalObject* globalObject, unsigned argsLength) +void GenericArgumentsImpl::initModifiedArgumentsDescriptorIfNecessary(JSGlobalObject* globalObject, unsigned argsLength) { if (!m_modifiedArgumentsDescriptor) initModifiedArgumentsDescriptor(globalObject, argsLength); } template -void GenericArguments::setModifiedArgumentDescriptor(JSGlobalObject* globalObject, unsigned index, unsigned length) +void GenericArgumentsImpl::setModifiedArgumentDescriptor(JSGlobalObject* globalObject, unsigned index, unsigned length) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -310,7 +310,7 @@ void GenericArguments::setModifiedArgumentDescriptor(JSGlobalObject* globa } template -bool GenericArguments::isModifiedArgumentDescriptor(unsigned index, unsigned length) +bool GenericArgumentsImpl::isModifiedArgumentDescriptor(unsigned index, unsigned length) { if (!m_modifiedArgumentsDescriptor) return false; @@ -320,7 +320,7 @@ bool GenericArguments::isModifiedArgumentDescriptor(unsigned index, unsign } template -void GenericArguments::copyToArguments(JSGlobalObject* globalObject, JSValue* firstElementDest, unsigned offset, unsigned length) +void GenericArgumentsImpl::copyToArguments(JSGlobalObject* globalObject, JSValue* firstElementDest, unsigned offset, unsigned length) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); diff --git a/Source/JavaScriptCore/runtime/ScopedArguments.cpp b/Source/JavaScriptCore/runtime/ScopedArguments.cpp index 80bbe38c83472..122d4cf0c3537 100644 --- a/Source/JavaScriptCore/runtime/ScopedArguments.cpp +++ b/Source/JavaScriptCore/runtime/ScopedArguments.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "ScopedArguments.h" -#include "GenericArgumentsInlines.h" +#include "GenericArgumentsImplInlines.h" WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN @@ -37,7 +37,7 @@ STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ScopedArguments); const ClassInfo ScopedArguments::s_info = { "Arguments"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ScopedArguments) }; ScopedArguments::ScopedArguments(VM& vm, Structure* structure, WriteBarrier* storage, unsigned totalLength, JSFunction* callee, ScopedArgumentsTable* table, JSLexicalEnvironment* scope) - : GenericArguments(vm, structure) + : GenericArgumentsImpl(vm, structure) , m_totalLength(totalLength) , m_callee(callee, WriteBarrierEarlyInit) , m_table(table, WriteBarrierEarlyInit) @@ -99,7 +99,7 @@ void ScopedArguments::visitChildrenImpl(JSCell* cell, Visitor& visitor) { ScopedArguments* thisObject = static_cast(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); - Base::visitChildren(thisObject, visitor); + GenericArgumentsImpl::visitChildren(thisObject, visitor); // Including Base::visitChildren. visitor.append(thisObject->m_callee); visitor.append(thisObject->m_table); @@ -159,7 +159,7 @@ void ScopedArguments::unmapArgument(JSGlobalObject* globalObject, uint32_t i) void ScopedArguments::copyToArguments(JSGlobalObject* globalObject, JSValue* firstElementDest, unsigned offset, unsigned length) { - GenericArguments::copyToArguments(globalObject, firstElementDest, offset, length); + GenericArgumentsImpl::copyToArguments(globalObject, firstElementDest, offset, length); } bool ScopedArguments::isIteratorProtocolFastAndNonObservable() diff --git a/Source/JavaScriptCore/runtime/ScopedArguments.h b/Source/JavaScriptCore/runtime/ScopedArguments.h index 63e6d3b23b361..a04606f3400d1 100644 --- a/Source/JavaScriptCore/runtime/ScopedArguments.h +++ b/Source/JavaScriptCore/runtime/ScopedArguments.h @@ -25,7 +25,7 @@ #pragma once -#include "GenericArguments.h" +#include "GenericArgumentsImpl.h" #include "JSLexicalEnvironment.h" #include "Watchpoint.h" @@ -39,10 +39,10 @@ namespace JSC { // object will store the overflow arguments, if there are any. This object will use the symbol // table's ScopedArgumentsTable and the activation, or its overflow storage, to handle all indexed // lookups. -class ScopedArguments final : public GenericArguments { +class ScopedArguments final : public GenericArgumentsImpl { private: ScopedArguments(VM&, Structure*, WriteBarrier* storage, unsigned totalLength, JSFunction* callee, ScopedArgumentsTable*, JSLexicalEnvironment*); - using Base = GenericArguments; + using Base = GenericArgumentsImpl; public: template @@ -139,17 +139,17 @@ class ScopedArguments final : public GenericArguments { void initModifiedArgumentsDescriptorIfNecessary(JSGlobalObject* globalObject) { - GenericArguments::initModifiedArgumentsDescriptorIfNecessary(globalObject, m_table->length()); + GenericArgumentsImpl::initModifiedArgumentsDescriptorIfNecessary(globalObject, m_table->length()); } void setModifiedArgumentDescriptor(JSGlobalObject* globalObject, unsigned index) { - GenericArguments::setModifiedArgumentDescriptor(globalObject, index, m_table->length()); + GenericArgumentsImpl::setModifiedArgumentDescriptor(globalObject, index, m_table->length()); } bool isModifiedArgumentDescriptor(unsigned index) { - return GenericArguments::isModifiedArgumentDescriptor(index, m_table->length()); + return GenericArgumentsImpl::isModifiedArgumentDescriptor(index, m_table->length()); } void copyToArguments(JSGlobalObject*, JSValue* firstElementDest, unsigned offset, unsigned length); From 441eaa11a2c6d54cd44ee145b4465fbec959f71c Mon Sep 17 00:00:00 2001 From: Ryan Fuller Date: Fri, 21 Feb 2025 17:42:23 -0800 Subject: [PATCH 019/174] [ Build-Failure ] Fix visionOS Build rdar://145334544 https://bugs.webkit.org/show_bug.cgi?id=288265 Unreviewed build fix. Enum not available on all SDKs. * Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm: (WebCore::VideoPresentationInterfaceIOS::cleanupFullscreen): Canonical link: https://commits.webkit.org/290840@main --- Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm b/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm index 47e060079efdf..837d3c4e59e86 100644 --- a/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm +++ b/Source/WebCore/platform/ios/VideoPresentationInterfaceIOS.mm @@ -596,7 +596,6 @@ @interface UIViewController () LOG(Fullscreen, "VideoPresentationInterfaceIOS::cleanupFullscreen(%p)", this); if (this->cleanupExternalPlayback()) { - ASSERT(m_currentMode == HTMLMediaElementEnums::VideoFullscreenModeNone); return; } From dfb8e11413ac661a8edc3576a86443523218ed19 Mon Sep 17 00:00:00 2001 From: Abrar Rahman Protyasha Date: Fri, 21 Feb 2025 18:01:33 -0800 Subject: [PATCH 020/174] REGRESSION(290757@main)[macOS Debug]: ASSERTION FAILED: ProcessCapabilities::canUseAcceleratedBuffers() in pdf tests (failure in EWS) https://bugs.webkit.org/show_bug.cgi?id=288264 rdar://145334282 Reviewed by Richard Robinson. We can't mix and match accelerated drawing, and accessing the platform context, because this tries to map IOSurfaces into the web content process, which is denied by the sandbox. This patch corrects the minor typo in layerNeedsPlatformContext since we want to return true if shouldUseInProcessBackingStore is true, and not if it's false. * Source/WebKit/WebProcess/Plugins/PDF/UnifiedPDF/UnifiedPDFPlugin.mm: (WebKit::UnifiedPDFPlugin::layerNeedsPlatformContext const): Canonical link: https://commits.webkit.org/290841@main --- .../WebProcess/Plugins/PDF/UnifiedPDF/UnifiedPDFPlugin.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/WebKit/WebProcess/Plugins/PDF/UnifiedPDF/UnifiedPDFPlugin.mm b/Source/WebKit/WebProcess/Plugins/PDF/UnifiedPDF/UnifiedPDFPlugin.mm index 3a3a15ef3a33e..27f2493e4e72c 100644 --- a/Source/WebKit/WebProcess/Plugins/PDF/UnifiedPDF/UnifiedPDFPlugin.mm +++ b/Source/WebKit/WebProcess/Plugins/PDF/UnifiedPDF/UnifiedPDFPlugin.mm @@ -4491,7 +4491,7 @@ static bool isEmpty(PDFSelection *selection) bool UnifiedPDFPlugin::layerNeedsPlatformContext(const GraphicsLayer* layer) const { - return !shouldUseInProcessBackingStore() && (layer == layerForHorizontalScrollbar() || layer == layerForVerticalScrollbar() || layer == layerForScrollCorner()); + return shouldUseInProcessBackingStore() && (layer == layerForHorizontalScrollbar() || layer == layerForVerticalScrollbar() || layer == layerForScrollCorner()); } ViewportConfiguration::Parameters UnifiedPDFPlugin::viewportParameters() From 18cc91853d356a9f1af0de8568930cb806c6e05e Mon Sep 17 00:00:00 2001 From: Timothy Hatcher Date: Fri, 21 Feb 2025 18:04:03 -0800 Subject: [PATCH 021/174] Refine the construction of WKWebExtension in Swift. https://webkit.org/b/288129 rdar://problem/145235810 Reviewed by Brian Weinstein and Richard Robinson. Introduce a Swift overlay for WKWebExtension to define two async init() methods that are more idiomatic in Swift. Swift really does not like _init methods, so unprefix the existing private init methods to keep the compiler happy when building the overlay. Retain prefixed versions for ObjC compatability with existing clients. * Source/WebKit/Configurations/Base.xcconfig: Define ENABLE_WEB_EXTENSION_API. * Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.h: * Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.mm: (-[WKWebExtension _initWithAppExtensionBundle:error:]): (-[WKWebExtension _initWithResourceBaseURL:error:]): (-[WKWebExtension _initWithAppExtensionBundle:resourceBaseURL:error:]): (-[WKWebExtension _initWithManifestDictionary:]): (-[WKWebExtension _initWithManifestDictionary:resources:]): (-[WKWebExtension _initWithResources:]): (-[WKWebExtension initWithAppExtensionBundle:error:]): (-[WKWebExtension initWithResourceBaseURL:error:]): (-[WKWebExtension initWithAppExtensionBundle:resourceBaseURL:error:]): (-[WKWebExtension initWithManifestDictionary:]): (-[WKWebExtension initWithManifestDictionary:resources:]): (-[WKWebExtension initWithResources:]): * Source/WebKit/UIProcess/API/Cocoa/WKWebExtensionPrivate.h: * Source/WebKit/UIProcess/API/Cocoa/WebKitSwiftOverlay.swift: Canonical link: https://commits.webkit.org/290842@main --- Source/WebKit/Configurations/Base.xcconfig | 27 +++++- .../UIProcess/API/Cocoa/WKWebExtension.h | 4 +- .../UIProcess/API/Cocoa/WKWebExtension.mm | 94 +++++++++++++------ .../API/Cocoa/WKWebExtensionPrivate.h | 21 +++-- .../API/Cocoa/WebKitSwiftOverlay.swift | 25 +++++ 5 files changed, 130 insertions(+), 41 deletions(-) diff --git a/Source/WebKit/Configurations/Base.xcconfig b/Source/WebKit/Configurations/Base.xcconfig index 76549bdf11915..258bd934e780d 100644 --- a/Source/WebKit/Configurations/Base.xcconfig +++ b/Source/WebKit/Configurations/Base.xcconfig @@ -139,8 +139,31 @@ WK_ENABLE_SWIFTUI[sdk=xros2*] = NO; WK_ENABLE_SWIFTUI[sdk=xrsimulator1*] = NO; WK_ENABLE_SWIFTUI[sdk=xrsimulator2*] = NO; -SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(SWIFT_ACTIVE_COMPILATION_CONDITIONS_$(WK_ENABLE_SWIFTUI)); -SWIFT_ACTIVE_COMPILATION_CONDITIONS_YES = ENABLE_SWIFTUI; +WK_ENABLE_WEB_EXTENSION_API = YES; +WK_ENABLE_WEB_EXTENSION_API[sdk=watch*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=appletv*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=mac*13*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=mac*14*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=mac*15.0*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=mac*15.1*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=mac*15.2*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=mac*15.3*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=iphone*17*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=iphone*18.0*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=iphone*18.1*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=iphone*18.2*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=iphone*18.3*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=xr*1*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=xr*2.0*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=xr*2.1*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=xr*2.2*] = NO; +WK_ENABLE_WEB_EXTENSION_API[sdk=xr*2.3*] = NO; + +SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(SWIFT_ACTIVE_COMPILATION_CONDITIONS_SWIFTUI_$(WK_ENABLE_SWIFTUI)) +SWIFT_ACTIVE_COMPILATION_CONDITIONS_SWIFTUI_YES = ENABLE_SWIFTUI + +SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(SWIFT_ACTIVE_COMPILATION_CONDITIONS_WEB_EXTENSION_API_$(WK_ENABLE_WEB_EXTENSION_API)) +SWIFT_ACTIVE_COMPILATION_CONDITIONS_WEB_EXTENSION_API_YES = ENABLE_WEB_EXTENSION_API OTHER_CPLUSPLUSFLAGS = $(inherited) -isystem $(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders; diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.h b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.h index ec09fbf7509ce..2764447a5433d 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.h +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.h @@ -82,7 +82,7 @@ WK_CLASS_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA), visionos(WK_XROS_TBA)) WK @discussion The app extension bundle must contain a `manifest.json` file in its resources directory. If the manifest is invalid or missing, or the bundle is otherwise improperly configured, an error will be returned. */ -+ (void)extensionWithAppExtensionBundle:(NSBundle *)appExtensionBundle completionHandler:(void (^)(WKWebExtension * WK_NULLABLE_RESULT extension, NSError * _Nullable error))completionHandler WK_SWIFT_ASYNC_THROWS_ON_FALSE(1); ++ (void)extensionWithAppExtensionBundle:(NSBundle *)appExtensionBundle completionHandler:(void (^)(WKWebExtension * _Nullable extension, NSError * _Nullable error))completionHandler NS_REFINED_FOR_SWIFT; /*! @abstract Returns a web extension initialized with a specified resource base URL, which can point to either a directory or a ZIP archive. @@ -91,7 +91,7 @@ WK_CLASS_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA), visionos(WK_XROS_TBA)) WK @discussion The URL must be a file URL that points to either a directory with a `manifest.json` file or a ZIP archive containing a `manifest.json` file. If the manifest is invalid or missing, or the URL points to an unsupported format or invalid archive, an error will be returned. */ -+ (void)extensionWithResourceBaseURL:(NSURL *)resourceBaseURL completionHandler:(void (^)(WKWebExtension * WK_NULLABLE_RESULT extension, NSError * _Nullable error))completionHandler WK_SWIFT_ASYNC_THROWS_ON_FALSE(1); ++ (void)extensionWithResourceBaseURL:(NSURL *)resourceBaseURL completionHandler:(void (^)(WKWebExtension * _Nullable extension, NSError * _Nullable error))completionHandler NS_REFINED_FOR_SWIFT; /*! @abstract An array of all errors that occurred during the processing of the extension. diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.mm b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.mm index d3c153560a6f3..279bd260386f6 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtension.mm @@ -86,29 +86,47 @@ + (void)extensionWithResourceBaseURL:(NSURL *)resourceBaseURL completionHandler: }); } -// FIXME: Remove after Safari has adopted new methods. -- (instancetype)initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error +- (instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error { - return [self _initWithAppExtensionBundle:appExtensionBundle error:error]; + return [self initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:nil error:error]; } -- (instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error +- (instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error { - return [self _initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:nil error:error]; + return [self initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; } -// FIXME: Remove after Safari has adopted new methods. -- (instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error +- (instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle resourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error { - return [self _initWithResourceBaseURL:resourceBaseURL error:error]; + return [self initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:resourceBaseURL error:error]; } -- (instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error +- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest +{ + return [self initWithManifestDictionary:manifest resources:nil]; +} + +- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources +{ + return [self initWithManifestDictionary:manifest resources:resources]; +} + +- (instancetype)_initWithResources:(NSDictionary *)resources { - return [self _initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; + return [self initWithResources:resources]; } -- (instancetype)_initWithAppExtensionBundle:(nullable NSBundle *)appExtensionBundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error +- (instancetype)initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error +{ + return [self initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:nil error:error]; +} + +- (instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error +{ + return [self initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; +} + +- (instancetype)initWithAppExtensionBundle:(nullable NSBundle *)appExtensionBundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error { NSParameterAssert(appExtensionBundle || resourceBaseURL); @@ -138,14 +156,14 @@ - (instancetype)_initWithAppExtensionBundle:(nullable NSBundle *)appExtensionBun return self; } -- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest +- (instancetype)initWithManifestDictionary:(NSDictionary *)manifest { NSParameterAssert([manifest isKindOfClass:NSDictionary.class]); - return [self _initWithManifestDictionary:manifest resources:nil]; + return [self initWithManifestDictionary:manifest resources:nil]; } -- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources +- (instancetype)initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources { NSParameterAssert([manifest isKindOfClass:NSDictionary.class]); @@ -157,7 +175,7 @@ - (instancetype)_initWithManifestDictionary:(NSDictionary *)mani return self; } -- (instancetype)_initWithResources:(NSDictionary *)resources +- (instancetype)initWithResources:(NSDictionary *)resources { NSParameterAssert([resources isKindOfClass:NSDictionary.class]); @@ -349,44 +367,64 @@ + (void)extensionWithResourceBaseURL:(NSURL *)resourceBaseURL completionHandler: completionHandler(nil, [NSError errorWithDomain:NSCocoaErrorDomain code:NSFeatureUnsupportedError userInfo:nil]); } -- (instancetype)initWithAppExtensionBundle:(NSBundle *)bundle error:(NSError **)error +- (instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error { - return [self _initWithAppExtensionBundle:bundle error:error]; + return [self initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:nil error:error]; } -- (instancetype)_initWithAppExtensionBundle:(NSBundle *)bundle error:(NSError **)error +- (instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error { - return [self _initWithAppExtensionBundle:bundle resourceBaseURL:nil error:error]; + return [self initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; } -- (instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error +- (instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle resourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error { - return [self _initWithResourceBaseURL:resourceBaseURL error:error]; + return [self initWithAppExtensionBundle:appExtensionBundle resourceBaseURL:resourceBaseURL error:error]; } -- (instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error +- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest { - return [self _initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; + return [self initWithManifestDictionary:manifest resources:nil]; } -- (instancetype)_initWithAppExtensionBundle:(nullable NSBundle *)bundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error +- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources +{ + return [self initWithManifestDictionary:manifest resources:resources]; +} + +- (instancetype)_initWithResources:(NSDictionary *)resources +{ + return [self initWithResources:resources]; +} + +- (instancetype)initWithAppExtensionBundle:(NSBundle *)bundle error:(NSError **)error +{ + return [self initWithAppExtensionBundle:bundle resourceBaseURL:nil error:error]; +} + +- (instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error +{ + return [self initWithAppExtensionBundle:nil resourceBaseURL:resourceBaseURL error:error]; +} + +- (instancetype)initWithAppExtensionBundle:(nullable NSBundle *)bundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error { if (error) *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFeatureUnsupportedError userInfo:nil]; return nil; } -- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest +- (instancetype)initWithManifestDictionary:(NSDictionary *)manifest { - return [self _initWithManifestDictionary:manifest resources:nil]; + return [self initWithManifestDictionary:manifest resources:nil]; } -- (instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources +- (instancetype)initWithManifestDictionary:(NSDictionary *)manifest resources:(NSDictionary *)resources { return nil; } -- (instancetype)_initWithResources:(NSDictionary *)resources +- (instancetype)initWithResources:(NSDictionary *)resources { return nil; } diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtensionPrivate.h b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtensionPrivate.h index 2e524adcc0fdb..6175d61ccf4fa 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebExtensionPrivate.h +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebExtensionPrivate.h @@ -29,9 +29,12 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @interface WKWebExtension () -// FIXME: Remove after Safari has adopted new methods. -- (nullable instancetype)initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error; -- (nullable instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error; +- (nullable instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error NS_SWIFT_UNAVAILABLE("Use init(appExtensionBundle:)."); +- (nullable instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error NS_SWIFT_UNAVAILABLE("Use init(resourceBaseURL:)."); +- (nullable instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle resourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error NS_SWIFT_UNAVAILABLE("Use init(appExtensionBundle:resourceBaseURL:)."); +- (nullable instancetype)_initWithManifestDictionary:(NSDictionary *)manifest NS_SWIFT_UNAVAILABLE("Use init(manifestDictionary:)."); +- (nullable instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(nullable NSDictionary *)resources NS_SWIFT_UNAVAILABLE("Use init(manifestDictionary:resources:)."); +- (nullable instancetype)_initWithResources:(NSDictionary *)resources NS_SWIFT_UNAVAILABLE("Use init(resources:)."); /*! @abstract Returns a web extension initialized with a specified app extension bundle. @@ -39,7 +42,7 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @param error Set to \c nil or an error instance if an error occurred. @result An initialized web extension, or `nil` if the object could not be initialized due to an error. */ -- (nullable instancetype)_initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error; +- (nullable instancetype)initWithAppExtensionBundle:(NSBundle *)appExtensionBundle error:(NSError **)error NS_REFINED_FOR_SWIFT; /*! @abstract Returns a web extension initialized with a specified resource base URL. @@ -48,7 +51,7 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @result An initialized web extension, or `nil` if the object could not be initialized due to an error. @discussion The URL must be a file URL that points to either a directory containing a `manifest.json` file or a valid ZIP archive. */ -- (nullable instancetype)_initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error; +- (nullable instancetype)initWithResourceBaseURL:(NSURL *)resourceBaseURL error:(NSError **)error NS_REFINED_FOR_SWIFT; /*! @abstract Returns a web extension initialized with a specified app extension bundle and resource base URL. @@ -59,14 +62,14 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @discussion Either the app extension bundle or the resource base URL (which can point to a directory or a valid ZIP archive) must be provided. This initializer is useful when the extension resources are in a different location from the app extension bundle used for native messaging. */ -- (nullable instancetype)_initWithAppExtensionBundle:(nullable NSBundle *)appExtensionBundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithAppExtensionBundle:(nullable NSBundle *)appExtensionBundle resourceBaseURL:(nullable NSURL *)resourceBaseURL error:(NSError **)error NS_DESIGNATED_INITIALIZER; /*! @abstract Returns a web extension initialized with a specified manifest dictionary. @param manifest The dictionary containing the manifest data for the web extension. @result An initialized web extension, or `nil` if the object could not be initialized due to an error. */ -- (nullable instancetype)_initWithManifestDictionary:(NSDictionary *)manifest; +- (nullable instancetype)initWithManifestDictionary:(NSDictionary *)manifest; /*! @abstract Returns a web extension initialized with a specified manifest dictionary and resources. @@ -76,7 +79,7 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @discussion The resources dictionary provides additional data required for the web extension. Paths in resources can have subdirectories, such as `_locales/en/messages.json`. */ -- (nullable instancetype)_initWithManifestDictionary:(NSDictionary *)manifest resources:(nullable NSDictionary *)resources NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithManifestDictionary:(NSDictionary *)manifest resources:(nullable NSDictionary *)resources NS_DESIGNATED_INITIALIZER; /*! @abstract Returns a web extension initialized with specified resources. @@ -85,7 +88,7 @@ WK_HEADER_AUDIT_BEGIN(nullability, sendability) @discussion The resources dictionary must provide at least the `manifest.json` resource. Paths in resources can have subdirectories, such as `_locales/en/messages.json`. */ -- (nullable instancetype)_initWithResources:(NSDictionary *)resources NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithResources:(NSDictionary *)resources NS_DESIGNATED_INITIALIZER; /*! @abstract A Boolean value indicating whether the extension background content is a service worker. */ @property (readonly, nonatomic) BOOL _hasServiceWorkerBackgroundContent; diff --git a/Source/WebKit/UIProcess/API/Cocoa/WebKitSwiftOverlay.swift b/Source/WebKit/UIProcess/API/Cocoa/WebKitSwiftOverlay.swift index 994071d3a2df9..4bf6a4475d26e 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WebKitSwiftOverlay.swift +++ b/Source/WebKit/UIProcess/API/Cocoa/WebKitSwiftOverlay.swift @@ -25,6 +25,10 @@ #if !os(tvOS) && !os(watchOS) +#if ENABLE_WEB_EXTENSION_API +internal import WebKit_Private.WKWebExtensionPrivate +#endif + #if USE_APPLE_INTERNAL_SDK @_spi(CTypeConversion) import Network #endif @@ -85,7 +89,25 @@ extension WKWebView { } #endif +#if ENABLE_WEB_EXTENSION_API @available(iOS 18.4, macOS 15.4, visionOS 2.4, *) +@available(watchOS, unavailable) +@available(tvOS, unavailable) +extension WKWebExtension { + public convenience init(appExtensionBundle: Bundle) async throws { + // FIXME: Make the WebExtension class load data on a background thread. + try self.init(appExtensionBundle: appExtensionBundle, resourceBaseURL: nil) + } + + public convenience init(resourceBaseURL: URL) async throws { + // FIXME: Make the WebExtension class load data on a background thread. + try self.init(appExtensionBundle: nil, resourceBaseURL: resourceBaseURL) + } +} + +@available(iOS 18.4, macOS 15.4, visionOS 2.4, *) +@available(watchOS, unavailable) +@available(tvOS, unavailable) extension WKWebExtensionController { public func didCloseTab(_ closedTab: WKWebExtensionTab, windowIsClosing: Bool = false) { __didClose(closedTab, windowIsClosing: windowIsClosing) @@ -101,6 +123,8 @@ extension WKWebExtensionController { } @available(iOS 18.4, macOS 15.4, visionOS 2.4, *) +@available(watchOS, unavailable) +@available(tvOS, unavailable) extension WKWebExtensionContext { public func didCloseTab(_ closedTab: WKWebExtensionTab, windowIsClosing: Bool = false) { __didClose(closedTab, windowIsClosing: windowIsClosing) @@ -114,6 +138,7 @@ extension WKWebExtensionContext { __didMove(movedTab, from: index, in: oldWindow) } } +#endif // FIXME: Need to declare ProxyConfiguration SPI in order to build and test // this with public SDKs (https://bugs.webkit.org/show_bug.cgi?id=280911). From 35d27af31ff0de0cf32b70ed86cf49ac80ef6a0b Mon Sep 17 00:00:00 2001 From: Charlie Wolfe Date: Fri, 21 Feb 2025 18:13:01 -0800 Subject: [PATCH 022/174] Remove NetworkProcess::webProcessIdentifierForConnection https://bugs.webkit.org/show_bug.cgi?id=288266 Reviewed by Alex Christensen. * Source/WebKit/NetworkProcess/NetworkProcess.cpp: (WebKit::NetworkProcess::webProcessIdentifierForConnection const): Deleted. * Source/WebKit/NetworkProcess/NetworkProcess.h: Canonical link: https://commits.webkit.org/290843@main --- Source/WebKit/NetworkProcess/NetworkProcess.cpp | 9 --------- Source/WebKit/NetworkProcess/NetworkProcess.h | 1 - 2 files changed, 10 deletions(-) diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.cpp b/Source/WebKit/NetworkProcess/NetworkProcess.cpp index 4ccc800172a28..40718b455299e 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.cpp +++ b/Source/WebKit/NetworkProcess/NetworkProcess.cpp @@ -2925,15 +2925,6 @@ void NetworkProcess::connectionToWebProcessClosed(IPC::Connection& connection, P session->protectedStorageManager()->stopReceivingMessageFromConnection(connection); } -std::optional NetworkProcess::webProcessIdentifierForConnection(IPC::Connection& connection) const -{ - for (auto& [processIdentifier, webConnection] : m_webProcessConnections) { - if (&webConnection->connection() == &connection) - return processIdentifier; - } - return std::nullopt; -} - NetworkConnectionToWebProcess* NetworkProcess::webProcessConnection(ProcessIdentifier identifier) const { return m_webProcessConnections.get(identifier); diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.h b/Source/WebKit/NetworkProcess/NetworkProcess.h index afeed5468d9eb..c6b478b2564a0 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.h +++ b/Source/WebKit/NetworkProcess/NetworkProcess.h @@ -382,7 +382,6 @@ class NetworkProcess final : public AuxiliaryProcess, private DownloadManager::C NetworkConnectionToWebProcess* webProcessConnection(WebCore::ProcessIdentifier) const; NetworkConnectionToWebProcess* webProcessConnection(const IPC::Connection&) const; - std::optional webProcessIdentifierForConnection(IPC::Connection&) const; WebCore::MessagePortChannelRegistry& messagePortChannelRegistry() { return m_messagePortChannelRegistry; } void setServiceWorkerFetchTimeoutForTesting(Seconds, CompletionHandler&&); From 71573d3520b6bd7615c5d441c31cab644216783e Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Fri, 21 Feb 2025 18:34:31 -0800 Subject: [PATCH 023/174] Unreviewed. Update safer C++ expectations. * Source/WTF/SaferCPPExpectations/UncountedCallArgsCheckerExpectations: Canonical link: https://commits.webkit.org/290844@main --- .../SaferCPPExpectations/UncountedCallArgsCheckerExpectations | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/WTF/SaferCPPExpectations/UncountedCallArgsCheckerExpectations b/Source/WTF/SaferCPPExpectations/UncountedCallArgsCheckerExpectations index ea187187aa394..9406390babae1 100644 --- a/Source/WTF/SaferCPPExpectations/UncountedCallArgsCheckerExpectations +++ b/Source/WTF/SaferCPPExpectations/UncountedCallArgsCheckerExpectations @@ -1,7 +1,6 @@ wtf/AutomaticThread.cpp wtf/JSONValues.cpp wtf/MemoryPressureHandler.cpp -wtf/NativePromise.h wtf/ParallelHelperPool.cpp wtf/PrintStream.h wtf/ThreadGroup.cpp From ad233b0aa06fc2ec5676b2a0547fb7b41ca9ae8f Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Fri, 21 Feb 2025 18:37:16 -0800 Subject: [PATCH 024/174] Address Safer CPP failures in HTMLDocumentParser https://bugs.webkit.org/show_bug.cgi?id=288219 Reviewed by Ryosuke Niwa. * Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations: * Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations: * Source/WebCore/html/parser/HTMLDocumentParser.cpp: (WebCore::HTMLDocumentParser::prepareToStopParsing): (WebCore::HTMLDocumentParser::isScheduledForResume const): (WebCore::HTMLDocumentParser::runScriptsForPausedTreeBuilder): (WebCore::HTMLDocumentParser::pumpTokenizerLoop): (WebCore::HTMLDocumentParser::pumpTokenizer): (WebCore::HTMLDocumentParser::insert): (WebCore::HTMLDocumentParser::append): (WebCore::HTMLDocumentParser::appendCurrentInputStreamToPreloadScannerAndScan): * Source/WebCore/html/parser/HTMLDocumentParserFastPath.cpp: * Source/WebCore/html/parser/HTMLTreeBuilder.cpp: (WebCore::HTMLTreeBuilder::protectedScriptToProcess const): * Source/WebCore/html/parser/HTMLTreeBuilder.h: Canonical link: https://commits.webkit.org/290845@main --- .../UncountedCallArgsCheckerExpectations | 2 - .../UncountedLocalVarsCheckerExpectations | 1 - .../html/parser/HTMLDocumentParser.cpp | 41 +++++++++++-------- .../parser/HTMLDocumentParserFastPath.cpp | 4 +- .../WebCore/html/parser/HTMLTreeBuilder.cpp | 5 +++ Source/WebCore/html/parser/HTMLTreeBuilder.h | 1 + 6 files changed, 31 insertions(+), 23 deletions(-) diff --git a/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations index 9394d7644125a..7901a5caa8d70 100644 --- a/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations @@ -808,8 +808,6 @@ html/canvas/WebGLVertexArrayObjectBase.cpp html/canvas/WebGLVertexArrayObjectOES.cpp html/parser/HTMLConstructionSite.cpp html/parser/HTMLConstructionSite.h -html/parser/HTMLDocumentParser.cpp -html/parser/HTMLDocumentParserFastPath.cpp html/parser/HTMLParserScheduler.cpp html/parser/HTMLScriptRunner.cpp html/parser/HTMLTreeBuilder.cpp diff --git a/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations index a035d2c5d7da6..ecca01894b88a 100644 --- a/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations @@ -434,7 +434,6 @@ html/canvas/WebGLProvokingVertex.cpp html/canvas/WebGLRenderingContextBase.cpp html/canvas/WebGLUtilities.h html/parser/HTMLConstructionSite.cpp -html/parser/HTMLDocumentParser.cpp html/shadow/DateTimeEditElement.cpp html/shadow/MediaControlTextTrackContainerElement.cpp html/shadow/SliderThumbElement.cpp diff --git a/Source/WebCore/html/parser/HTMLDocumentParser.cpp b/Source/WebCore/html/parser/HTMLDocumentParser.cpp index 884aa4132cfc3..ac728514e6c65 100644 --- a/Source/WebCore/html/parser/HTMLDocumentParser.cpp +++ b/Source/WebCore/html/parser/HTMLDocumentParser.cpp @@ -142,10 +142,11 @@ void HTMLDocumentParser::prepareToStopParsing() // We will not have a scriptRunner when parsing a DocumentFragment. if (m_scriptRunner) { - document()->setReadyState(Document::ReadyState::Interactive); + RefPtr document = this->document(); + document->setReadyState(Document::ReadyState::Interactive); if (!isDetached()) - document()->processInternalResourceLinks(); + document->processInternalResourceLinks(); } // Setting the ready state above can fire mutation event and detach us @@ -204,7 +205,8 @@ void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode) bool HTMLDocumentParser::isScheduledForResume() const { - return m_parserScheduler && m_parserScheduler->isScheduledForResume(); + RefPtr scheduler = m_parserScheduler; + return scheduler && scheduler->isScheduledForResume(); } // Used by HTMLParserScheduler @@ -230,13 +232,14 @@ void HTMLDocumentParser::runScriptsForPausedTreeBuilder() // https://html.spec.whatwg.org/#create-an-element-for-the-token { // Prevent document.open/write during reactions by allocating the incrementer before the reactions stack. - ThrowOnDynamicMarkupInsertionCountIncrementer incrementer(*document()); + RefPtr document = this->document(); + ThrowOnDynamicMarkupInsertionCountIncrementer incrementer(*document); - document()->eventLoop().performMicrotaskCheckpoint(); + document->eventLoop().performMicrotaskCheckpoint(); - CustomElementReactionStack reactionStack(document()->globalObject()); - auto& elementInterface = constructionData->elementInterface.get(); - auto newElement = elementInterface.constructElementWithFallback(*document(), constructionData->registry, constructionData->name, + CustomElementReactionStack reactionStack(document->globalObject()); + Ref elementInterface = constructionData->elementInterface.get(); + auto newElement = elementInterface->constructElementWithFallback(*document, constructionData->registry, constructionData->name, m_scriptRunner && !m_scriptRunner->isExecutingScript() ? ParserConstructElementWithEmptyStack::Yes : ParserConstructElementWithEmptyStack::No); m_treeBuilder->didCreateCustomOrFallbackElement(WTFMove(newElement), *constructionData); } @@ -266,7 +269,7 @@ bool HTMLDocumentParser::pumpTokenizerLoop(SynchronousMode mode, bool parsingFra RefPtr parserScheduler = m_parserScheduler; do { if (UNLIKELY(isWaitingForScripts())) { - if (mode == SynchronousMode::AllowYield && parserScheduler->shouldYieldBeforeExecutingScript(m_treeBuilder->scriptToProcess(), session)) + if (mode == SynchronousMode::AllowYield && parserScheduler->shouldYieldBeforeExecutingScript(m_treeBuilder->protectedScriptToProcess().get(), session)) return true; runScriptsForPausedTreeBuilder(); @@ -279,7 +282,7 @@ bool HTMLDocumentParser::pumpTokenizerLoop(SynchronousMode mode, bool parsingFra // how the parser has always handled stopping when the page assigns window.location. What should // happen instead is that assigning window.location causes the parser to stop parsing cleanly. // The problem is we're not prepared to do that at every point where we run JavaScript. - if (UNLIKELY(!parsingFragment && document()->frame() && document()->frame()->protectedNavigationScheduler()->locationChangePending())) + if (UNLIKELY(!parsingFragment && document()->frame() && document()->protectedFrame()->protectedNavigationScheduler()->locationChangePending())) return false; if (UNLIKELY(mode == SynchronousMode::AllowYield && parserScheduler->shouldYieldBeforeToken(session))) @@ -327,17 +330,18 @@ void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) if (shouldResume) Ref { *m_parserScheduler }->scheduleForResume(); + RefPtr document = this->document(); if (isWaitingForScripts() && !isDetached()) { ASSERT(m_tokenizer.isInDataState()); if (!m_preloadScanner) { - m_preloadScanner = makeUnique(m_options, document()->url(), document()->deviceScaleFactor()); + m_preloadScanner = makeUnique(m_options, document->url(), document->deviceScaleFactor()); m_preloadScanner->appendToEnd(m_input.current()); } - m_preloadScanner->scan(*m_preloader, Ref { *document() }); + m_preloadScanner->scan(*m_preloader, *document); } // The viewport definition is known here, so we can load link preloads with media attributes. - if (document()->loader()) - LinkLoader::loadLinksFromHeader(document()->loader()->response().httpHeaderField(HTTPHeaderName::Link), document()->url(), *document(), LinkLoader::MediaAttributeCheck::MediaAttributeNotEmpty); + if (document->loader()) + LinkLoader::loadLinksFromHeader(document->loader()->response().httpHeaderField(HTTPHeaderName::Link), document->url(), *document, LinkLoader::MediaAttributeCheck::MediaAttributeNotEmpty); } void HTMLDocumentParser::constructTreeFromHTMLToken(HTMLTokenizer::TokenPtr& rawToken) @@ -386,12 +390,13 @@ void HTMLDocumentParser::insert(SegmentedString&& source) pumpTokenizerIfPossible(SynchronousMode::ForceSynchronous); if (isWaitingForScripts() && !isDetached()) { + RefPtr document = this->document(); // Check the document.write() output with a separate preload scanner as // the main scanner can't deal with insertions. if (!m_insertionPreloadScanner) - m_insertionPreloadScanner = makeUnique(m_options, document()->url(), document()->deviceScaleFactor()); + m_insertionPreloadScanner = makeUnique(m_options, document->url(), document->deviceScaleFactor()); m_insertionPreloadScanner->appendToEnd(source); - m_insertionPreloadScanner->scan(*m_preloader, *document()); + m_insertionPreloadScanner->scan(*m_preloader, *document); } endIfDelayed(); @@ -426,7 +431,7 @@ void HTMLDocumentParser::append(RefPtr&& inputSource, SynchronousMod } else { m_preloadScanner->appendToEnd(source); if (isWaitingForScripts()) - m_preloadScanner->scan(*m_preloader, *document()); + m_preloadScanner->scan(*m_preloader, *protectedDocument()); } } @@ -571,7 +576,7 @@ void HTMLDocumentParser::appendCurrentInputStreamToPreloadScannerAndScan() { ASSERT(m_preloadScanner); m_preloadScanner->appendToEnd(m_input.current()); - m_preloadScanner->scan(*m_preloader, *document()); + m_preloadScanner->scan(*m_preloader, *protectedDocument()); } void HTMLDocumentParser::notifyFinished(PendingScript& pendingScript) diff --git a/Source/WebCore/html/parser/HTMLDocumentParserFastPath.cpp b/Source/WebCore/html/parser/HTMLDocumentParserFastPath.cpp index 0d0dc0b3cf9c0..880abf6b4ce8f 100644 --- a/Source/WebCore/html/parser/HTMLDocumentParserFastPath.cpp +++ b/Source/WebCore/html/parser/HTMLDocumentParserFastPath.cpp @@ -230,8 +230,8 @@ class HTMLFastPathParser { HTMLFastPathResult parseResult() const { return m_parseResult; } private: - Ref m_document; - WeakRef m_destinationParent; + const Ref m_document; + const Ref m_destinationParent; StringParsingBuffer m_parsingBuffer; diff --git a/Source/WebCore/html/parser/HTMLTreeBuilder.cpp b/Source/WebCore/html/parser/HTMLTreeBuilder.cpp index f3c7644e45851..64d213d254cda 100644 --- a/Source/WebCore/html/parser/HTMLTreeBuilder.cpp +++ b/Source/WebCore/html/parser/HTMLTreeBuilder.cpp @@ -3146,4 +3146,9 @@ bool HTMLTreeBuilder::isOnStackOfOpenElements(Element& element) const return m_tree.openElements().contains(element); } +RefPtr HTMLTreeBuilder::protectedScriptToProcess() const +{ + return m_scriptToProcess; +} + } diff --git a/Source/WebCore/html/parser/HTMLTreeBuilder.h b/Source/WebCore/html/parser/HTMLTreeBuilder.h index c4201989a5e14..ef418499a3720 100644 --- a/Source/WebCore/html/parser/HTMLTreeBuilder.h +++ b/Source/WebCore/html/parser/HTMLTreeBuilder.h @@ -71,6 +71,7 @@ class HTMLTreeBuilder { // Must be called to take the parser-blocking script before calling the parser again. RefPtr takeScriptToProcess(TextPosition& scriptStartPosition); const ScriptElement* scriptToProcess() const { return m_scriptToProcess.get(); } + RefPtr protectedScriptToProcess() const; std::unique_ptr takeCustomElementConstructionData() { return WTFMove(m_customElementToConstruct); } void didCreateCustomOrFallbackElement(Ref&&, CustomElementConstructionData&); From 5b3c393e88a115165f8ccfdd1e60a7b266fa0ab7 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Fri, 21 Feb 2025 19:20:07 -0800 Subject: [PATCH 025/174] Fix static analyzer warnings in WebLocalFrameLoaderClient.cpp https://bugs.webkit.org/show_bug.cgi?id=288258 Reviewed by Chris Dumez. Deployed more smart pointers to address the unexpected static analyzer warnings. * Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp: (WebKit::WebLocalFrameLoaderClient::restoreViewState): (WebKit::WebLocalFrameLoaderClient::objectContentType): Canonical link: https://commits.webkit.org/290846@main --- .../WebCoreSupport/WebLocalFrameLoaderClient.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp b/Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp index 8beed6ae14e86..39d88ce2c7ba1 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp @@ -1415,13 +1415,14 @@ void WebLocalFrameLoaderClient::restoreViewState() double scaleFactor = m_localFrame->loader().history().protectedCurrentItem()->pageScaleFactor(); // A scale factor of 0 means the history item has the default scale factor, thus we do not need to update it. - if (scaleFactor) - m_frame->page()->send(Messages::WebPageProxy::PageScaleFactorDidChange(scaleFactor)); + RefPtr page = m_frame->page(); + if (page && scaleFactor) + page->send(Messages::WebPageProxy::PageScaleFactorDidChange(scaleFactor)); // FIXME: This should not be necessary. WebCore should be correctly invalidating // the view on restores from the back/forward cache. - if (m_frame->page() && m_frame.ptr() == &m_frame->page()->mainWebFrame()) - m_frame->page()->protectedDrawingArea()->setNeedsDisplay(); + if (page && m_frame.ptr() == &page->mainWebFrame()) + page->protectedDrawingArea()->setNeedsDisplay(); #endif } @@ -1665,7 +1666,7 @@ ObjectContentType WebLocalFrameLoaderClient::objectContentType(const URL& url, c if (mimeType.isEmpty()) { // Check if there's a plug-in around that can handle the extension. if (RefPtr webPage = m_frame->page()) { - if (pluginSupportsExtension(webPage->protectedCorePage()->pluginData(), extension)) + if (pluginSupportsExtension(webPage->protectedCorePage()->protectedPluginData(), extension)) return ObjectContentType::PlugIn; } return ObjectContentType::Frame; @@ -1682,7 +1683,7 @@ ObjectContentType WebLocalFrameLoaderClient::objectContentType(const URL& url, c if (RefPtr webPage = m_frame->page()) { auto allowedPluginTypes = PluginData::OnlyApplicationPlugins; - if (webPage->corePage()->protectedPluginData()->supportsMimeType(mimeType, allowedPluginTypes)) + if (webPage->protectedCorePage()->protectedPluginData()->supportsMimeType(mimeType, allowedPluginTypes)) return ObjectContentType::PlugIn; } From e31e39e35b2e3e92598bd6de2548d64d8bd3fa42 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Fri, 21 Feb 2025 19:21:28 -0800 Subject: [PATCH 026/174] Address Safer CPP failures in QueuedVideoOutput.mm https://bugs.webkit.org/show_bug.cgi?id=288224 Reviewed by Ryosuke Niwa. * Source/WebCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations: * Source/WebCore/platform/graphics/avfoundation/objc/QueuedVideoOutput.mm: (isType): (SPECIALIZE_TYPE_TRAITS_END): (-[WebQueuedVideoOutputDelegate outputMediaDataWillChange:]): (-[WebQueuedVideoOutputDelegate outputSequenceWasFlushed:]): Canonical link: https://commits.webkit.org/290847@main --- .../MemoryUnsafeCastCheckerExpectations | 1 - .../graphics/avfoundation/objc/QueuedVideoOutput.mm | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/WebCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations b/Source/WebCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations index f9c7db1ca5ecf..0db34133fab3e 100644 --- a/Source/WebCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations @@ -111,7 +111,6 @@ platform/encryptedmedia/clearkey/CDMClearKey.cpp platform/graphics/GraphicsLayer.cpp platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.h platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.h -platform/graphics/avfoundation/objc/QueuedVideoOutput.mm platform/graphics/ca/GraphicsLayerCA.cpp platform/graphics/ca/cocoa/PlatformCAAnimationCocoa.mm platform/graphics/mac/controls/ControlFactoryMac.mm diff --git a/Source/WebCore/platform/graphics/avfoundation/objc/QueuedVideoOutput.mm b/Source/WebCore/platform/graphics/avfoundation/objc/QueuedVideoOutput.mm index 07c7021a6a85a..cda959ab22cce 100644 --- a/Source/WebCore/platform/graphics/avfoundation/objc/QueuedVideoOutput.mm +++ b/Source/WebCore/platform/graphics/avfoundation/objc/QueuedVideoOutput.mm @@ -37,6 +37,10 @@ #include +SPECIALIZE_TYPE_TRAITS_BEGIN(AVPlayerItemVideoOutput) +static bool isType(const AVPlayerItemOutput& output) { return [&output isKindOfClass:PAL::getAVPlayerItemVideoOutputClass()]; } +SPECIALIZE_TYPE_TRAITS_END() + @interface WebQueuedVideoOutputDelegate : NSObject { WeakPtr _parent; } @@ -59,8 +63,7 @@ - (id)initWithParent:(WebCore::QueuedVideoOutput*)parent - (void)outputMediaDataWillChange:(AVPlayerItemOutput *)output { - ASSERT([output isKindOfClass:PAL::getAVPlayerItemVideoOutputClass()]); - auto* videoOutput = (AVPlayerItemVideoOutput*)output; + auto* videoOutput = downcast(output); Vector videoFrameEntries; do { @@ -86,8 +89,7 @@ - (void)outputMediaDataWillChange:(AVPlayerItemOutput *)output - (void)outputSequenceWasFlushed:(AVPlayerItemOutput *)output { - ASSERT([output isKindOfClass:PAL::getAVPlayerItemVideoOutputClass()]); - auto* videoOutput = (AVPlayerItemVideoOutput*)output; + auto* videoOutput = downcast(output); [videoOutput requestNotificationOfMediaDataChangeAsSoonAsPossible]; callOnMainRunLoop([parent = _parent] { From b14ff3e3b4c9d4cf77082b9523b7ca4fbc8fd344 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Fri, 21 Feb 2025 19:22:45 -0800 Subject: [PATCH 027/174] Address safer CPP failures in ViewSnapshotStore.cpp https://bugs.webkit.org/show_bug.cgi?id=288230 Reviewed by Ryosuke Niwa. * Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations: * Source/WebKit/UIProcess/ViewSnapshotStore.cpp: (WebKit::ViewSnapshotStore::pruneSnapshots): (WebKit::ViewSnapshotStore::discardSnapshotImages): Canonical link: https://commits.webkit.org/290848@main --- .../SaferCPPExpectations/UncountedCallArgsCheckerExpectations | 1 - Source/WebKit/UIProcess/ViewSnapshotStore.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations b/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations index 665f39c161df0..97d81ad726d2e 100644 --- a/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations +++ b/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations @@ -196,7 +196,6 @@ UIProcess/RemotePageProxy.cpp UIProcess/SpeechRecognitionPermissionManager.cpp UIProcess/SuspendedPageProxy.cpp UIProcess/UserMediaPermissionRequestManagerProxy.cpp -UIProcess/ViewSnapshotStore.cpp UIProcess/WebAuthentication/AuthenticatorManager.cpp UIProcess/WebAuthentication/Cocoa/LocalService.mm UIProcess/WebAuthentication/Cocoa/NfcService.mm diff --git a/Source/WebKit/UIProcess/ViewSnapshotStore.cpp b/Source/WebKit/UIProcess/ViewSnapshotStore.cpp index bf0cf4a125740..05dcea72bc6ea 100644 --- a/Source/WebKit/UIProcess/ViewSnapshotStore.cpp +++ b/Source/WebKit/UIProcess/ViewSnapshotStore.cpp @@ -77,7 +77,7 @@ void ViewSnapshotStore::pruneSnapshots(WebPageProxy& webPageProxy) // FIXME: We have enough information to do smarter-than-LRU eviction (making use of the back-forward lists, etc.) - m_snapshotsWithImages.first()->clearImage(); + Ref { m_snapshotsWithImages.first().get() }->clearImage(); } void ViewSnapshotStore::recordSnapshot(WebPageProxy& webPageProxy, WebBackForwardListItem& item) @@ -108,7 +108,7 @@ void ViewSnapshotStore::recordSnapshot(WebPageProxy& webPageProxy, WebBackForwar void ViewSnapshotStore::discardSnapshotImages() { while (!m_snapshotsWithImages.isEmpty()) - m_snapshotsWithImages.first()->clearImage(); + Ref { m_snapshotsWithImages.first().get() }->clearImage(); } void ViewSnapshotStore::discardSnapshotImagesForOrigin(const WebCore::SecurityOriginData& origin) From 23d60ee22b40b58ee7f3604d94d57cc479ce83f1 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Fri, 21 Feb 2025 19:25:13 -0800 Subject: [PATCH 028/174] Address Safer CPP failures in WebBackForwardCache https://bugs.webkit.org/show_bug.cgi?id=288225 Reviewed by Geoffrey Garen. * Source/WebKit/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations: * Source/WebKit/UIProcess/WebBackForwardCache.cpp: (WebKit::WebBackForwardCache::removeEntriesMatching): (WebKit::WebBackForwardCache::clear): * Source/WebKit/UIProcess/WebBackForwardCacheEntry.cpp: (WebKit::WebBackForwardCacheEntry::expirationTimerFired): Canonical link: https://commits.webkit.org/290849@main --- .../UncountedLocalVarsCheckerExpectations | 2 -- Source/WebKit/UIProcess/WebBackForwardCache.cpp | 10 +++++----- Source/WebKit/UIProcess/WebBackForwardCacheEntry.cpp | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Source/WebKit/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations b/Source/WebKit/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations index 038230958f093..ee68bee997fdc 100644 --- a/Source/WebKit/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations +++ b/Source/WebKit/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations @@ -36,8 +36,6 @@ UIProcess/Notifications/WebNotificationManagerProxy.cpp UIProcess/UserMediaPermissionRequestManagerProxy.cpp UIProcess/WebAuthentication/AuthenticatorManager.cpp UIProcess/WebAuthentication/Cocoa/NfcService.mm -UIProcess/WebBackForwardCache.cpp -UIProcess/WebBackForwardCacheEntry.cpp UIProcess/WebPreferences.cpp UIProcess/WebProcessCache.cpp UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm diff --git a/Source/WebKit/UIProcess/WebBackForwardCache.cpp b/Source/WebKit/UIProcess/WebBackForwardCache.cpp index a05ea56b133c7..23ca8743262f5 100644 --- a/Source/WebKit/UIProcess/WebBackForwardCache.cpp +++ b/Source/WebKit/UIProcess/WebBackForwardCache.cpp @@ -170,11 +170,11 @@ void WebBackForwardCache::removeEntriesForPageAndProcess(WebPageProxy& page, Web void WebBackForwardCache::removeEntriesMatching(NOESCAPE const Function& matches) { Vector> itemsWithEntriesToClear; - for (auto& item : m_itemsWithCachedPage) { + for (Ref item : m_itemsWithCachedPage) { if (matches(item)) - itemsWithEntriesToClear.append(item); + itemsWithEntriesToClear.append(WTFMove(item)); } - for (auto& item : itemsWithEntriesToClear) { + for (Ref item : itemsWithEntriesToClear) { m_itemsWithCachedPage.remove(item.get()); item->setBackForwardCacheEntry(nullptr); } @@ -184,8 +184,8 @@ void WebBackForwardCache::clear() { RELEASE_LOG(BackForwardCache, "WebBackForwardCache::clear"); auto itemsWithCachedPage = WTFMove(m_itemsWithCachedPage); - for (auto& item : itemsWithCachedPage) - item.setBackForwardCacheEntry(nullptr); + for (Ref item : itemsWithCachedPage) + item->setBackForwardCacheEntry(nullptr); } void WebBackForwardCache::pruneToSize(unsigned newSize) diff --git a/Source/WebKit/UIProcess/WebBackForwardCacheEntry.cpp b/Source/WebKit/UIProcess/WebBackForwardCacheEntry.cpp index ff038fdde378b..0df22cbb3de05 100644 --- a/Source/WebKit/UIProcess/WebBackForwardCacheEntry.cpp +++ b/Source/WebKit/UIProcess/WebBackForwardCacheEntry.cpp @@ -87,7 +87,7 @@ void WebBackForwardCacheEntry::expirationTimerFired() { ASSERT(m_backForwardItemID); RELEASE_LOG(BackForwardCache, "%p - WebBackForwardCacheEntry::expirationTimerFired backForwardItemID=%s, hasSuspendedPage=%d", this, m_backForwardItemID->toString().utf8().data(), !!m_suspendedPage); - auto* item = WebBackForwardListItem::itemForID(*m_backForwardItemID); + RefPtr item = WebBackForwardListItem::itemForID(*m_backForwardItemID); ASSERT(item); if (RefPtr backForwardCache = m_backForwardCache.get()) backForwardCache->removeEntry(*item); From b47957d7891a2b461cf18eb5e627f8e368626905 Mon Sep 17 00:00:00 2001 From: Basuke Suzuki Date: Fri, 21 Feb 2025 19:38:58 -0800 Subject: [PATCH 029/174] [ResourceMonitor] Add more meaningful release log information part 1. https://bugs.webkit.org/show_bug.cgi?id=288171 rdar://145259890 Reviewed by Per Arne Vollan and Chris Dumez. Adding new log category and move current release logs to that category. And also add more meaningful information for the record. This is part 1, targeting WebCore. In this change, chances are: - URL checks are most of two times per each iframe. - One check generates several logs. - When it hits the network limits, some extra logs is produced. - If unloading happens, ~10 more logs. One exception is for shared worker, which logs all network usage and this will be rare case. In case it causes an issue, this will be removed. For now, it is required to observe the usage during development. * LayoutTests/http/tests/iframe-monitor/dark-mode-expected.txt: * LayoutTests/http/tests/iframe-monitor/eligibility-expected.txt: * LayoutTests/http/tests/iframe-monitor/iframe-unload-expected.txt: * LayoutTests/http/tests/iframe-monitor/shared-worker-expected.txt: * LayoutTests/http/tests/iframe-monitor/throttler-expected.txt: * Source/WebCore/dom/Document.cpp: (WebCore::Document::resourceMonitor): * Source/WebCore/loader/ResourceLoadInfo.cpp: (WebCore::ContentExtensions::resourceTypeToString): * Source/WebCore/loader/ResourceLoadInfo.h: * Source/WebCore/loader/ResourceMonitor.cpp: (WebCore::ResourceMonitor::setEligibility): (WebCore::ResourceMonitor::didReceiveResponse): (WebCore::eligibilityToString): (WebCore::ResourceMonitor::continueAfterDidReceiveEligibility): * Source/WebCore/loader/ResourceMonitor.h: * Source/WebCore/loader/ResourceMonitorChecker.cpp: (WebCore::ResourceMonitorChecker::checkEligibility): * Source/WebCore/loader/ResourceMonitorPersistence.cpp: (WebCore::ResourceMonitorPersistence::reportSQLError): (WebCore::ResourceMonitorPersistence::openDatabase): * Source/WebCore/loader/ResourceMonitorThrottler.cpp: (WebCore::ResourceMonitorThrottler::~ResourceMonitorThrottler): (WebCore::ResourceMonitorThrottler::tryAccess): * Source/WebCore/page/LocalFrame.cpp: (WebCore::LocalFrame::showResourceMonitoringError): (WebCore::LocalFrame::reportResourceMonitoringWarning): * Source/WebCore/platform/Logging.h: * Source/WebCore/workers/shared/SharedWorker.cpp: (WebCore::SharedWorker::reportNetworkUsage): Canonical link: https://commits.webkit.org/290850@main --- .../iframe-monitor/dark-mode-expected.txt | 6 ++-- .../iframe-monitor/eligibility-expected.txt | 2 +- .../iframe-monitor/iframe-unload-expected.txt | 2 +- .../iframe-monitor/shared-worker-expected.txt | 4 +-- .../iframe-monitor/throttler-expected.txt | 10 +++--- Source/WebCore/dom/Document.cpp | 6 ++-- Source/WebCore/loader/ResourceLoadInfo.cpp | 34 +++++++++++++++++++ Source/WebCore/loader/ResourceLoadInfo.h | 2 ++ Source/WebCore/loader/ResourceMonitor.cpp | 31 ++++++++++++++--- Source/WebCore/loader/ResourceMonitor.h | 3 +- .../WebCore/loader/ResourceMonitorChecker.cpp | 4 +-- .../loader/ResourceMonitorPersistence.cpp | 6 ++-- .../loader/ResourceMonitorThrottler.cpp | 17 ++++++++-- Source/WebCore/page/LocalFrame.cpp | 6 ++-- Source/WebCore/platform/Logging.h | 1 + .../WebCore/workers/shared/SharedWorker.cpp | 4 ++- 16 files changed, 108 insertions(+), 30 deletions(-) diff --git a/LayoutTests/http/tests/iframe-monitor/dark-mode-expected.txt b/LayoutTests/http/tests/iframe-monitor/dark-mode-expected.txt index 708662c1638a2..d086e32f45df5 100644 --- a/LayoutTests/http/tests/iframe-monitor/dark-mode-expected.txt +++ b/LayoutTests/http/tests/iframe-monitor/dark-mode-expected.txt @@ -1,6 +1,6 @@ -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://127.0.0.1:8000/iframe-monitor/resources/--eligible--/iframe.html +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://127.0.0.1:8000/iframe-monitor/resources/--eligible--/iframe.html +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://127.0.0.1:8000/iframe-monitor/resources/--eligible--/iframe.html Test unloaded HTML supports dark mode correctly. On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". diff --git a/LayoutTests/http/tests/iframe-monitor/eligibility-expected.txt b/LayoutTests/http/tests/iframe-monitor/eligibility-expected.txt index 9258fbb9b648c..fb5caa5ae6476 100644 --- a/LayoutTests/http/tests/iframe-monitor/eligibility-expected.txt +++ b/LayoutTests/http/tests/iframe-monitor/eligibility-expected.txt @@ -1,4 +1,4 @@ -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://localhost:8080/iframe-monitor/resources/--eligible--/iframe.html Test resource monitor. On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". diff --git a/LayoutTests/http/tests/iframe-monitor/iframe-unload-expected.txt b/LayoutTests/http/tests/iframe-monitor/iframe-unload-expected.txt index 4bd3a346d730d..f72cd9305fd52 100644 --- a/LayoutTests/http/tests/iframe-monitor/iframe-unload-expected.txt +++ b/LayoutTests/http/tests/iframe-monitor/iframe-unload-expected.txt @@ -1,4 +1,4 @@ -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://localhost:8080/iframe-monitor/resources/--eligible--/iframe.html Test iframe with huge resource usage is unloaded. On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". diff --git a/LayoutTests/http/tests/iframe-monitor/shared-worker-expected.txt b/LayoutTests/http/tests/iframe-monitor/shared-worker-expected.txt index b868ca131fedf..733f7692b24f7 100644 --- a/LayoutTests/http/tests/iframe-monitor/shared-worker-expected.txt +++ b/LayoutTests/http/tests/iframe-monitor/shared-worker-expected.txt @@ -5,8 +5,8 @@ CONSOLE MESSAGE: Launched shared worker CONSOLE MESSAGE: iframe is ready CONSOLE MESSAGE: Get message from parent window CONSOLE MESSAGE: Send fetch request to worker -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://localhost:8080/iframe-monitor/resources/--eligible--/shared-worker.html +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://localhost:8080/iframe-monitor/resources/--eligible--/shared-worker.html Test iframes using same shared worker are unloaded. On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". diff --git a/LayoutTests/http/tests/iframe-monitor/throttler-expected.txt b/LayoutTests/http/tests/iframe-monitor/throttler-expected.txt index d61768888902b..883517849ae6b 100644 --- a/LayoutTests/http/tests/iframe-monitor/throttler-expected.txt +++ b/LayoutTests/http/tests/iframe-monitor/throttler-expected.txt @@ -1,8 +1,8 @@ -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. -CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit. +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://localhost:8080/iframe-monitor/resources/--eligible--/iframe.html +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://localhost:8080/iframe-monitor/resources/--eligible--/iframe.html +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://localhost:8080/iframe-monitor/resources/--eligible--/iframe.html +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://localhost:8080/iframe-monitor/resources/--eligible--/iframe.html +CONSOLE MESSAGE: Frame was unloaded because its network usage exceeded the limit: 4194304 bytes, url=http://localhost:8080/iframe-monitor/resources/--eligible--/iframe.html CONSOLE MESSAGE: Frame's network usage exceeded the limit. Test throttler prevents unloaded. diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp index 818eaadfa9646..7f4225ce8672d 100644 --- a/Source/WebCore/dom/Document.cpp +++ b/Source/WebCore/dom/Document.cpp @@ -413,7 +413,7 @@ #include "HTMLVideoElement.h" #endif -#define DOCUMENT_RELEASE_LOG(channel, fmt, ...) RELEASE_LOG(channel, "%p - [pageID=%" PRIu64 ", frameID=%" PRIu64 ", isMainFrame=%d] Document::" fmt, this, pageID() ? pageID()->toUInt64() : 0, frameID() ? frameID()->object().toUInt64() : 0, this == &topDocument(), ##__VA_ARGS__) +#define DOCUMENT_RELEASE_LOG(channel, fmt, ...) RELEASE_LOG(channel, "%p - [pageID=%" PRIu64 ", frameID=%" PRIu64 ", isMainFrame=%d] Document::" fmt, this, pageID() ? pageID()->toUInt64() : 0, frameID() ? frameID()->object().toUInt64() : 0, this->isTopDocument(), ##__VA_ARGS__) #define DOCUMENT_RELEASE_LOG_ERROR(channel, fmt, ...) RELEASE_LOG_ERROR(channel, "%p - [pageID=%" PRIu64 ", frameID=%" PRIu64 ", isMainFrame=%d] Document::" fmt, this, pageID() ? pageID()->toUInt64() : 0, frameID() ? frameID()->object().toUInt64() : 0, this->isTopDocument(), ##__VA_ARGS__) namespace WebCore { @@ -11331,8 +11331,10 @@ ResourceMonitor& Document::resourceMonitor() { ASSERT(!frame()->isMainFrame()); - if (!m_resourceMonitor) + if (!m_resourceMonitor) { m_resourceMonitor = ResourceMonitor::create(*frame()); + DOCUMENT_RELEASE_LOG(ResourceMonitoring, "ResourceMonitor is created for the document."); + } return *m_resourceMonitor.get(); } diff --git a/Source/WebCore/loader/ResourceLoadInfo.cpp b/Source/WebCore/loader/ResourceLoadInfo.cpp index c14a0b49ea798..9b0a072a6d5ce 100644 --- a/Source/WebCore/loader/ResourceLoadInfo.cpp +++ b/Source/WebCore/loader/ResourceLoadInfo.cpp @@ -159,6 +159,40 @@ ResourceFlags ResourceLoadInfo::getResourceFlags() const return flags; } +ASCIILiteral resourceTypeToString(OptionSet resourceTypes) +{ + switch (*resourceTypes.begin()) { + case ResourceType::Document: + return "document"_s; + case ResourceType::Image: + return "image"_s; + case ResourceType::StyleSheet: + return "style-sheet"_s; + case ResourceType::Script: + return "script"_s; + case ResourceType::Font: + return "font"_s; + case ResourceType::WebSocket: + return "websocket"_s; + case ResourceType::Fetch: + return "fetch"_s; + case ResourceType::SVGDocument: + return "svg-document"_s; + case ResourceType::Media: + return "media"_s; + case ResourceType::Popup: + return "popup"_s; + case ResourceType::Ping: + return "ping"_s; + case ResourceType::CSPReport: + return "csp-report"_s; + case ResourceType::Other: + return "other"_s; + default: + return "raw"_s; + } +} + } // namespace WebCore::ContentExtensions #endif // ENABLE(CONTENT_EXTENSIONS) diff --git a/Source/WebCore/loader/ResourceLoadInfo.h b/Source/WebCore/loader/ResourceLoadInfo.h index 01ce3d870f0a9..74c3b3808a3af 100644 --- a/Source/WebCore/loader/ResourceLoadInfo.h +++ b/Source/WebCore/loader/ResourceLoadInfo.h @@ -85,6 +85,8 @@ std::optional> readResourceType(StringView); std::optional> readLoadType(StringView); std::optional> readLoadContext(StringView); +ASCIILiteral resourceTypeToString(OptionSet); + struct ResourceLoadInfo { URL resourceURL; URL mainDocumentURL; diff --git a/Source/WebCore/loader/ResourceMonitor.cpp b/Source/WebCore/loader/ResourceMonitor.cpp index 8be7372cc4f4b..64995badb39d9 100644 --- a/Source/WebCore/loader/ResourceMonitor.cpp +++ b/Source/WebCore/loader/ResourceMonitor.cpp @@ -41,7 +41,7 @@ namespace WebCore { #if ENABLE(CONTENT_EXTENSIONS) -#define RESOURCEMONITOR_RELEASE_LOG(fmt, ...) RELEASE_LOG(ResourceLoading, "%p - ResourceMonitor(frame %p)::" fmt, this, m_frame.get(), ##__VA_ARGS__) +#define RESOURCEMONITOR_RELEASE_LOG(fmt, ...) RELEASE_LOG(ResourceMonitoring, "ResourceMonitor(frame %" PRIu64 ")::" fmt, m_frame->frameID().object().toUInt64(), ##__VA_ARGS__) Ref ResourceMonitor::create(LocalFrame& frame) { @@ -61,9 +61,10 @@ void ResourceMonitor::setEligibility(Eligibility eligibility) return; m_eligibility = eligibility; - RESOURCEMONITOR_RELEASE_LOG("The frame is %" PUBLIC_LOG_STRING ".", (eligibility == Eligibility::Eligible ? "eligible" : "not eligible")); if (isEligible()) { + RESOURCEMONITOR_RELEASE_LOG("Frame (%" SENSITIVE_LOG_STRING ") was set as eligible.", m_frameURL.string().utf8().data()); + if (RefPtr resourceMonitor = parentResourceMonitorIfExists(); !resourceMonitor || !resourceMonitor->isEligible()) checkNetworkUsageExcessIfNecessary(); } @@ -104,12 +105,34 @@ void ResourceMonitor::didReceiveResponse(const URL& url, OptionSetsetEligibility(eligibility); + protectedThis->continueAfterDidReceiveEligibility(eligibility, url, resourceType); }); } +static ASCIILiteral eligibilityToString(ResourceMonitorEligibility eligibility) +{ + return eligibility == ResourceMonitorEligibility::Eligible ? "eligible"_s : "not eligible"_s; +} + +void ResourceMonitor::continueAfterDidReceiveEligibility(Eligibility eligibility, const URL& url, OptionSet resourceType) +{ + RefPtr frame = m_frame.get(); + RefPtr page = frame ? frame->mainFrame().page() : nullptr; + if (!page) + return; + + RESOURCEMONITOR_RELEASE_LOG("resourceURL %" SENSITIVE_LOG_STRING " mainDocumentURL %" SENSITIVE_LOG_STRING " frameURL %" SENSITIVE_LOG_STRING " (%" PUBLIC_LOG_STRING ") is set as %" PUBLIC_LOG_STRING ".", + url.string().utf8().data(), + page->mainFrameURL().string().utf8().data(), + m_frameURL.string().utf8().data(), + ContentExtensions::resourceTypeToString(resourceType).characters(), + eligibilityToString(eligibility).characters() + ); + setEligibility(eligibility); +} + void ResourceMonitor::addNetworkUsage(size_t bytes) { if (m_networkUsageExceed) diff --git a/Source/WebCore/loader/ResourceMonitor.h b/Source/WebCore/loader/ResourceMonitor.h index 1dce59e3f1eef..f68330a033090 100644 --- a/Source/WebCore/loader/ResourceMonitor.h +++ b/Source/WebCore/loader/ResourceMonitor.h @@ -46,12 +46,13 @@ class ResourceMonitor final : public RefCountedAndCanMakeWeakPtr); WEBCORE_EXPORT void addNetworkUsage(size_t); private: explicit ResourceMonitor(LocalFrame&); + void didReceiveResponse(const URL&, OptionSet); + void continueAfterDidReceiveEligibility(Eligibility, const URL&, OptionSet); void checkNetworkUsageExcessIfNecessary(); ResourceMonitor* parentResourceMonitorIfExists() const; diff --git a/Source/WebCore/loader/ResourceMonitorChecker.cpp b/Source/WebCore/loader/ResourceMonitorChecker.cpp index f54d445642a60..2a2eaa6d50272 100644 --- a/Source/WebCore/loader/ResourceMonitorChecker.cpp +++ b/Source/WebCore/loader/ResourceMonitorChecker.cpp @@ -33,7 +33,7 @@ #if ENABLE(CONTENT_EXTENSIONS) -#define RESOURCEMONITOR_RELEASE_LOG(fmt, ...) RELEASE_LOG(ResourceLoading, "%p - ResourceMonitorChecker::" fmt, this, ##__VA_ARGS__) +#define RESOURCEMONITOR_RELEASE_LOG(fmt, ...) RELEASE_LOG(ResourceMonitoring, "ResourceMonitorChecker::" fmt, ##__VA_ARGS__) namespace WebCore { @@ -95,7 +95,7 @@ ResourceMonitorChecker::Eligibility ResourceMonitorChecker::checkEligibility(con ASSERT(m_ruleList); auto matched = m_ruleList->processContentRuleListsForResourceMonitoring(info.resourceURL, info.mainDocumentURL, info.frameURL, info.type); - RESOURCEMONITOR_RELEASE_LOG("The url is %" PUBLIC_LOG_STRING ": %" SENSITIVE_LOG_STRING, (matched ? "eligible" : "not eligible"), info.resourceURL.string().utf8().data()); + RESOURCEMONITOR_RELEASE_LOG("The url is %" PUBLIC_LOG_STRING ": %" SENSITIVE_LOG_STRING " (%" PUBLIC_LOG_STRING ")", (matched ? "eligible" : "not eligible"), info.resourceURL.string().utf8().data(), ContentExtensions::resourceTypeToString(info.type).characters()); return matched ? Eligibility::Eligible : Eligibility::NotEligible; } diff --git a/Source/WebCore/loader/ResourceMonitorPersistence.cpp b/Source/WebCore/loader/ResourceMonitorPersistence.cpp index 48aa94ce362aa..494c087cd6ddb 100644 --- a/Source/WebCore/loader/ResourceMonitorPersistence.cpp +++ b/Source/WebCore/loader/ResourceMonitorPersistence.cpp @@ -35,7 +35,7 @@ #if ENABLE(CONTENT_EXTENSIONS) -#define RESOURCEMONITOR_RELEASE_LOG(fmt, ...) RELEASE_LOG(ResourceLoading, "%p - ResourceMonitorPersistence::" fmt, this, ##__VA_ARGS__) +#define RESOURCEMONITOR_RELEASE_LOG(fmt, ...) RELEASE_LOG(ResourceMonitoring, "ResourceMonitorPersistence::" fmt, ##__VA_ARGS__) namespace WebCore { @@ -78,7 +78,7 @@ static ContinuousApproximateTime doubleToContinuousApproximateTime(double timest void ResourceMonitorPersistence::reportSQLError(ASCIILiteral method, ASCIILiteral action) { - RESOURCEMONITOR_RELEASE_LOG("%" PUBLIC_LOG_STRING ": Failed to %" PUBLIC_LOG_STRING " (%d) - %" PUBLIC_LOG_STRING, method.characters(), action.characters(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + RELEASE_LOG_ERROR(ResourceMonitoring, "ResourceMonitorPersistence::%" PUBLIC_LOG_STRING ": Failed to %" PUBLIC_LOG_STRING " (%d) - %" PUBLIC_LOG_STRING, method.characters(), action.characters(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); } bool ResourceMonitorPersistence::openDatabase(String&& path) @@ -90,7 +90,7 @@ bool ResourceMonitorPersistence::openDatabase(String&& path) m_sqliteDB = makeUnique(); auto reportErrorAndCloseDatabase = [&](ASCIILiteral action) { - RESOURCEMONITOR_RELEASE_LOG("openDatabase: Failed to %" PUBLIC_LOG_STRING " at path '%" PUBLIC_LOG_STRING "' (%d) - %" PUBLIC_LOG_STRING, action.characters(), path.utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + reportSQLError("openDatabase"_s, action); closeDatabase(); return false; }; diff --git a/Source/WebCore/loader/ResourceMonitorThrottler.cpp b/Source/WebCore/loader/ResourceMonitorThrottler.cpp index c2bb9d16bd855..65aaf46feacc8 100644 --- a/Source/WebCore/loader/ResourceMonitorThrottler.cpp +++ b/Source/WebCore/loader/ResourceMonitorThrottler.cpp @@ -34,6 +34,8 @@ #if ENABLE(CONTENT_EXTENSIONS) +#define RESOURCEMONITOR_RELEASE_LOG(fmt, ...) RELEASE_LOG(ResourceMonitoring, "ResourceMonitorThrottler::" fmt, ##__VA_ARGS__) + namespace WebCore { ResourceMonitorThrottler::ResourceMonitorThrottler(String&& path, size_t count, Seconds duration, size_t maxHosts) @@ -42,13 +44,18 @@ ResourceMonitorThrottler::ResourceMonitorThrottler(String&& path, size_t count, ASSERT(!isMainThread()); ASSERT(maxHosts >= 1); + RESOURCEMONITOR_RELEASE_LOG("Throttler is launching, count=%zu, duration=%f, maxHosts=%zu", count, duration.value(), maxHosts); + RESOURCEMONITOR_RELEASE_LOG("Opening persistence for throttler."); auto persistence = makeUnique(); - if (!persistence->openDatabase(WTFMove(path))) + if (!persistence->openDatabase(WTFMove(path))) { + RESOURCEMONITOR_RELEASE_LOG("Failed to setup persistence for throttler."); return; + } m_persistence = WTFMove(persistence); + RESOURCEMONITOR_RELEASE_LOG("Success to setup persistence for throttler."); auto now = ContinuousApproximateTime::now(); m_persistence->deleteExpiredRecords(now, m_config.duration); @@ -67,6 +74,7 @@ ResourceMonitorThrottler::~ResourceMonitorThrottler() ASSERT(!isMainThread()); if (m_persistence) { + RESOURCEMONITOR_RELEASE_LOG("Closing persistence for throttler."); m_persistence->deleteExpiredRecords(ContinuousApproximateTime::now(), m_config.duration); m_persistence = nullptr; } @@ -108,10 +116,13 @@ bool ResourceMonitorThrottler::tryAccess(const String& host, ContinuousApproxima bool wasGranted = throttler.tryAccessAndUpdateHistory(time, m_config); if (wasGranted) { + RESOURCEMONITOR_RELEASE_LOG("Throttler granted for unloading the resource for %" PUBLIC_LOG_STRING ".", host.utf8().data()); + maintainHosts(time); if (m_persistence) m_persistence->recordAccess(host, time); - } + } else + RESOURCEMONITOR_RELEASE_LOG("Throttler denied for unloading the resource for %" PUBLIC_LOG_STRING ".", host.utf8().data()); return wasGranted; } @@ -191,4 +202,6 @@ bool ResourceMonitorThrottler::AccessThrottler::tryExpire(ContinuousApproximateT } // namespace WebCore +#undef RESOURCEMONITOR_RELEASE_LOG + #endif diff --git a/Source/WebCore/page/LocalFrame.cpp b/Source/WebCore/page/LocalFrame.cpp index aa599fb3573d6..f335cc07b0a88 100644 --- a/Source/WebCore/page/LocalFrame.cpp +++ b/Source/WebCore/page/LocalFrame.cpp @@ -1463,9 +1463,9 @@ void LocalFrame::showResourceMonitoringError() if (RefPtr page = protectedPage()) mainFrameURL = page->mainFrameURL(); - FRAME_RELEASE_LOG(ResourceLoading, "Detected excessive network usage in frame at %" SENSITIVE_LOG_STRING " and main frame at %" SENSITIVE_LOG_STRING ": unloading", url.isValid() ? url.string().utf8().data() : "invalid", mainFrameURL.isValid() ? mainFrameURL.string().utf8().data() : "invalid"); + FRAME_RELEASE_LOG(ResourceMonitoring, "Detected excessive network usage in frame at %" SENSITIVE_LOG_STRING " and main frame at %" SENSITIVE_LOG_STRING ": unloading", url.isValid() ? url.string().utf8().data() : "invalid", mainFrameURL.isValid() ? mainFrameURL.string().utf8().data() : "invalid"); - document->addConsoleMessage(MessageSource::ContentBlocker, MessageLevel::Error, "Frame was unloaded because its network usage exceeded the limit."_s); + document->addConsoleMessage(MessageSource::ContentBlocker, MessageLevel::Error, makeString("Frame was unloaded because its network usage exceeded the limit: "_s, ResourceMonitorChecker::networkUsageThreshold, " bytes, url="_s, url.string())); for (RefPtr frame = this; frame; frame = frame->tree().traverseNext()) { if (RefPtr localFrame = dynamicDowncast(frame)) { @@ -1493,7 +1493,7 @@ void LocalFrame::reportResourceMonitoringWarning() if (RefPtr page = protectedPage()) mainFrameURL = page->mainFrameURL(); - FRAME_RELEASE_LOG(ResourceLoading, "Detected excessive network usage in frame at %" SENSITIVE_LOG_STRING " and main frame at %" SENSITIVE_LOG_STRING ": not unloading due to global limits", url.isValid() ? url.string().utf8().data() : "invalid", mainFrameURL.isValid() ? mainFrameURL.string().utf8().data() : "invalid"); + FRAME_RELEASE_LOG(ResourceMonitoring, "Detected excessive network usage in frame at %" SENSITIVE_LOG_STRING " and main frame at %" SENSITIVE_LOG_STRING ": not unloading due to global limits", url.isValid() ? url.string().utf8().data() : "invalid", mainFrameURL.isValid() ? mainFrameURL.string().utf8().data() : "invalid"); if (RefPtr document = this->document()) document->addConsoleMessage(MessageSource::ContentBlocker, MessageLevel::Warning, "Frame's network usage exceeded the limit."_s); diff --git a/Source/WebCore/platform/Logging.h b/Source/WebCore/platform/Logging.h index 21df84b606056..e297884483c13 100644 --- a/Source/WebCore/platform/Logging.h +++ b/Source/WebCore/platform/Logging.h @@ -157,6 +157,7 @@ namespace WebCore { M(ResourceLoading) \ M(ResourceLoadObserver) \ M(ResourceLoadStatistics) \ + M(ResourceMonitoring) \ M(ScrollAnimations) \ M(ScrollAnchoring) \ M(ScrollSnap) \ diff --git a/Source/WebCore/workers/shared/SharedWorker.cpp b/Source/WebCore/workers/shared/SharedWorker.cpp index 9a697d36d6b64..79ba586f21bc7 100644 --- a/Source/WebCore/workers/shared/SharedWorker.cpp +++ b/Source/WebCore/workers/shared/SharedWorker.cpp @@ -200,8 +200,10 @@ void SharedWorker::reportNetworkUsage(size_t bytesTransferredOverNetwork) ASSERT(!delta.hasOverflowed()); if (delta) { - if (RefPtr resourceMonitor = m_resourceMonitor) + if (RefPtr resourceMonitor = m_resourceMonitor) { + RELEASE_LOG(ResourceMonitoring, "[identifier=%" PUBLIC_LOG_STRING "] SharedWorker::reportNetworkUsage to ResourceMonitor: %zu bytes", identifier().toString().utf8().data(), delta.value()); resourceMonitor->addNetworkUsage(delta); + } } #endif From 647aeaeed0cbcd4394f3466b6a0a30a7b4643b4a Mon Sep 17 00:00:00 2001 From: Per Arne Vollan Date: Fri, 21 Feb 2025 20:27:01 -0800 Subject: [PATCH 030/174] Silence syscall sandbox violations https://bugs.webkit.org/show_bug.cgi?id=288251 rdar://144654297 Reviewed by Chris Dumez. Silence some yscall sandbox violations in the WebContent and Networking process that we have never allowed. * Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb.in: * Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.WebContent.sb.in: Canonical link: https://commits.webkit.org/290851@main --- .../SandboxProfiles/ios/com.apple.WebKit.Networking.sb.in | 2 ++ .../SandboxProfiles/ios/com.apple.WebKit.WebContent.sb.in | 1 + 2 files changed, 3 insertions(+) diff --git a/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb.in b/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb.in index a86f844c408d1..d5166e179d93f 100644 --- a/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb.in +++ b/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb.in @@ -710,6 +710,8 @@ (when (defined? 'SYS_crossarch_trap) (deny syscall-unix (with no-report) (syscall-number SYS_crossarch_trap))) + (deny syscall-unix (with no-report) (syscall-number + SYS___nexus_set_opt)) (allow syscall-unix (syscall-number SYS___channel_get_info SYS___channel_open diff --git a/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.WebContent.sb.in b/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.WebContent.sb.in index d19c35f165220..4ddc8b65db3bd 100644 --- a/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.WebContent.sb.in +++ b/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.WebContent.sb.in @@ -1120,6 +1120,7 @@ (deny syscall-unix (with no-report) (syscall-number SYS_connect + SYS_persona #if !PLATFORM(WATCHOS) SYS_sigreturn #endif From cd1c41746636cab4395e29106473e3dcea9c0817 Mon Sep 17 00:00:00 2001 From: Per Arne Vollan Date: Fri, 21 Feb 2025 20:29:19 -0800 Subject: [PATCH 031/174] Notification observer is not being removed https://bugs.webkit.org/show_bug.cgi?id=288140 rdar://145054761 Reviewed by Chris Dumez. Notification observer is not being removed in WebProcessPool. This can cause crashes if the notification is being posted after the WebProcessPool has been destroyed. * Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm: (WebKit::WebProcessPool::unregisterNotificationObservers): Canonical link: https://commits.webkit.org/290852@main --- Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm index 9cf62c259e9c3..d14a7dfa79daa 100644 --- a/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm +++ b/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm @@ -985,7 +985,6 @@ static void logProcessPoolState(const WebProcessPool& pool) [[NSWorkspace.sharedWorkspace notificationCenter] removeObserver:m_accessibilityDisplayOptionsNotificationObserver.get()]; [[NSNotificationCenter defaultCenter] removeObserver:m_scrollerStyleNotificationObserver.get()]; [[NSNotificationCenter defaultCenter] removeObserver:m_deactivationObserver.get()]; - [[NSNotificationCenter defaultCenter] removeObserver:m_finishedMobileAssetFontDownloadObserver.get()]; removeCFNotificationObserver(AppleColorPreferencesChangedNotification, CFNotificationCenterGetDistributedCenter()); for (auto token : m_openDirectoryNotifyTokens) notify_cancel(token); @@ -1011,7 +1010,9 @@ static void logProcessPoolState(const WebProcessPool& pool) [[NSNotificationCenter defaultCenter] removeObserver:m_activationObserver.get()]; m_powerSourceNotifier = nullptr; - + + [[NSNotificationCenter defaultCenter] removeObserver:m_finishedMobileAssetFontDownloadObserver.get()]; + #if PLATFORM(COCOA) removeCFNotificationObserver((__bridge CFStringRef)WKLockdownModeContainerConfigurationChangedNotification); #endif From 32731b2f8e9688471cd6b5431e0fa710bdcf10b7 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Fri, 21 Feb 2025 20:31:44 -0800 Subject: [PATCH 032/174] Address safer CPP failures in IDBBindingUtilities.cpp https://bugs.webkit.org/show_bug.cgi?id=288232 Reviewed by Ryosuke Niwa. * Source/WebCore/SaferCPPExpectations/NoUncountedMemberCheckerExpectations: * Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations: * Source/WebCore/bindings/js/IDBBindingUtilities.cpp: (WebCore::get): (WebCore::deserializeIDBValueToJSValue): (WebCore::IDBSerializationContext::IDBSerializationContext): (WebCore::IDBSerializationContext::~IDBSerializationContext): (WebCore::IDBSerializationContext::globalObject): Canonical link: https://commits.webkit.org/290853@main --- .../NoUncountedMemberCheckerExpectations | 1 - .../UncountedCallArgsCheckerExpectations | 1 - .../bindings/js/IDBBindingUtilities.cpp | 23 +++++++++++-------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Source/WebCore/SaferCPPExpectations/NoUncountedMemberCheckerExpectations b/Source/WebCore/SaferCPPExpectations/NoUncountedMemberCheckerExpectations index ca3589dcaa254..c19a3bce2decd 100644 --- a/Source/WebCore/SaferCPPExpectations/NoUncountedMemberCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/NoUncountedMemberCheckerExpectations @@ -11,7 +11,6 @@ Modules/webdatabase/DatabaseTask.h Modules/webdatabase/SQLTransactionBackend.h accessibility/AXCoreObject.h accessibility/AXSearchManager.h -bindings/js/IDBBindingUtilities.cpp bindings/js/JSEventTargetCustom.h bindings/js/JSLazyEventListener.cpp css/CSSFontFaceSource.h diff --git a/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations index 7901a5caa8d70..17b0b1fbfa039 100644 --- a/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations @@ -306,7 +306,6 @@ bindings/js/CachedModuleScriptLoader.cpp bindings/js/CommonVM.cpp bindings/js/DOMPromiseProxy.h bindings/js/DOMWrapperWorld.cpp -bindings/js/IDBBindingUtilities.cpp bindings/js/JSAudioBufferCustom.cpp bindings/js/JSAudioBufferSourceNodeCustom.cpp bindings/js/JSAudioWorkletGlobalScopeCustom.cpp diff --git a/Source/WebCore/bindings/js/IDBBindingUtilities.cpp b/Source/WebCore/bindings/js/IDBBindingUtilities.cpp index dd3b1bb435afc..82288c56a5900 100644 --- a/Source/WebCore/bindings/js/IDBBindingUtilities.cpp +++ b/Source/WebCore/bindings/js/IDBBindingUtilities.cpp @@ -83,7 +83,7 @@ static bool get(JSGlobalObject& lexicalGlobalObject, JSValue object, const Strin } if (obj->inherits() && (keyPathElement == "size"_s || keyPathElement == "type"_s)) { if (keyPathElement == "size"_s) { - result = jsNumber(jsCast(obj)->wrapped().size()); + result = jsNumber(jsCast(obj)->protectedWrapped()->size()); return true; } if (keyPathElement == "type"_s) { @@ -97,11 +97,11 @@ static bool get(JSGlobalObject& lexicalGlobalObject, JSValue object, const Strin return true; } if (keyPathElement == "lastModified"_s) { - result = jsNumber(jsCast(obj)->wrapped().lastModified()); + result = jsNumber(jsCast(obj)->protectedWrapped()->lastModified()); return true; } if (keyPathElement == "lastModifiedDate"_s) { - result = jsDate(lexicalGlobalObject, WallTime::fromRawSeconds(Seconds::fromMilliseconds(jsCast(obj)->wrapped().lastModified()).value())); + result = jsDate(lexicalGlobalObject, WallTime::fromRawSeconds(Seconds::fromMilliseconds(jsCast(obj)->protectedWrapped()->lastModified()).value())); return true; } } @@ -405,10 +405,11 @@ static JSValue deserializeIDBValueToJSValue(JSGlobalObject& lexicalGlobalObject, auto serializedValue = SerializedScriptValue::createFromWireBytes(Vector(data)); - lexicalGlobalObject.vm().apiLock().lock(); + Ref apiLock = lexicalGlobalObject.vm().apiLock(); + apiLock->lock(); Vector> messagePorts; JSValue result = serializedValue->deserialize(lexicalGlobalObject, &globalObject, messagePorts, value.blobURLs(), value.blobFilePaths(), SerializationErrorMode::NonThrowing); - lexicalGlobalObject.vm().apiLock().unlock(); + apiLock->unlock(); return result; } @@ -529,13 +530,15 @@ std::optional deserializeIDBValueWithKeyInjection(JSGlobalObject& class IDBSerializationContext { public: IDBSerializationContext() - : m_thread(Thread::current()) +#if ASSERT_ENABLED + : m_threadUID(Thread::current().uid()) +#endif { } ~IDBSerializationContext() { - ASSERT(&m_thread == &Thread::current()); + ASSERT(m_threadUID == Thread::current().uid()); if (!m_vm) return; @@ -546,7 +549,7 @@ class IDBSerializationContext { JSC::JSGlobalObject& globalObject() { - ASSERT(&m_thread == &Thread::current()); + ASSERT(m_threadUID == Thread::current().uid()); initializeVM(); return *m_globalObject.get(); @@ -569,7 +572,9 @@ class IDBSerializationContext { RefPtr m_vm; JSC::Strong m_globalObject; - Thread& m_thread; +#if ASSERT_ENABLED + uint32_t m_threadUID; +#endif }; void callOnIDBSerializationThreadAndWait(Function&& function) From 8149eb6146fa9b5a9f42a30ca52a4d5c211b54b4 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Fri, 21 Feb 2025 20:36:19 -0800 Subject: [PATCH 033/174] `FullscreenManager::exitRemovedFullscreenElement()` should not use `fullscreenOrPendingElement()` https://bugs.webkit.org/show_bug.cgi?id=287278 rdar://144401606 Reviewed by Brent Fulgham and Darin Adler. Since the element has a fullscreen flag set, it's never going to be the pending element. Stop using `fullscreenOrPendingElement()` to reduce usage of the inaccurate `m_fullscreenElement`. * LayoutTests/imported/w3c/web-platform-tests/fullscreen/model/move-to-fullscreen-iframe-expected.txt: * Source/WebCore/dom/FullscreenManager.cpp: (WebCore::FullscreenManager::exitRemovedFullscreenElement): Canonical link: https://commits.webkit.org/290854@main --- .../fullscreen/model/move-to-fullscreen-iframe-expected.txt | 4 +--- Source/WebCore/dom/FullscreenManager.cpp | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/LayoutTests/imported/w3c/web-platform-tests/fullscreen/model/move-to-fullscreen-iframe-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fullscreen/model/move-to-fullscreen-iframe-expected.txt index 254ab105dad91..8d15a783f6e89 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/fullscreen/model/move-to-fullscreen-iframe-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/fullscreen/model/move-to-fullscreen-iframe-expected.txt @@ -1,5 +1,3 @@ -Harness Error (TIMEOUT), message = null - -TIMEOUT Moving fullscreen document's body into a fullscreen iframe Test timed out +PASS Moving fullscreen document's body into a fullscreen iframe diff --git a/Source/WebCore/dom/FullscreenManager.cpp b/Source/WebCore/dom/FullscreenManager.cpp index 08f5e00c03020..ec54c1bd169c7 100644 --- a/Source/WebCore/dom/FullscreenManager.cpp +++ b/Source/WebCore/dom/FullscreenManager.cpp @@ -656,8 +656,7 @@ void FullscreenManager::exitRemovedFullscreenElement(Element& element) { ASSERT(element.hasFullscreenFlag()); - auto fullscreenElement = fullscreenOrPendingElement(); - if (fullscreenElement == &element) { + if (fullscreenElement() == &element) { INFO_LOG(LOGIDENTIFIER, "Fullscreen element removed; exiting fullscreen"); exitFullscreen([] (auto) { }); } else From 9c1ec887f19b98af90245127c68af6143efe868b Mon Sep 17 00:00:00 2001 From: Alex Christensen Date: Fri, 21 Feb 2025 21:03:33 -0800 Subject: [PATCH 034/174] Remove FullscreenManager's m_pendingFullscreenElement https://bugs.webkit.org/show_bug.cgi?id=288263 rdar://145333462 Reviewed by Tim Nguyen. After all the CompletionHandler work I've done recently, it's not needed any more. * Source/WebCore/dom/FullscreenManager.cpp: (WebCore::FullscreenManager::requestFullscreenForElement): (WebCore::FullscreenManager::cancelFullscreen): (WebCore::FullscreenManager::exitFullscreen): (WebCore::FullscreenManager::willEnterFullscreen): (WebCore::FullscreenManager::willExitFullscreen): (WebCore::FullscreenManager::didExitFullscreen): (WebCore::FullscreenManager::clear): * Source/WebCore/dom/FullscreenManager.h: Canonical link: https://commits.webkit.org/290855@main --- Source/WebCore/dom/FullscreenManager.cpp | 53 +++++------------------- Source/WebCore/dom/FullscreenManager.h | 3 -- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/Source/WebCore/dom/FullscreenManager.cpp b/Source/WebCore/dom/FullscreenManager.cpp index ec54c1bd169c7..d4f550f73715b 100644 --- a/Source/WebCore/dom/FullscreenManager.cpp +++ b/Source/WebCore/dom/FullscreenManager.cpp @@ -165,18 +165,11 @@ void FullscreenManager::requestFullscreenForElement(Ref&& element, Full INFO_LOG(identifier); - m_pendingFullscreenElement = RefPtr { element.ptr() }; - protectedDocument()->eventLoop().queueTask(TaskSource::MediaElement, [this, weakThis = WeakPtr { *this }, element = WTFMove(element), completionHandler = WTFMove(completionHandler), hasKeyboardAccess, fullscreenElementReadyCheck, handleError, identifier, mode] () mutable { CheckedPtr checkedThis = weakThis.get(); if (!checkedThis) return completionHandler(Exception { ExceptionCode::TypeError }); - // Don't allow fullscreen if it has been cancelled or a different fullscreen element - // has requested fullscreen. - if (m_pendingFullscreenElement != element.ptr()) - return handleError("Fullscreen request aborted by a fullscreen request for another element."_s, EmitErrorEvent::Yes, WTFMove(completionHandler)); - // Don't allow fullscreen if we're inside an exitFullscreen operation. if (m_pendingExitFullscreen) return handleError("Fullscreen request aborted by a request to exit fullscreen."_s, EmitErrorEvent::Yes, WTFMove(completionHandler)); @@ -217,7 +210,7 @@ void FullscreenManager::requestFullscreenForElement(Ref&& element, Full return completionHandler(Exception { ExceptionCode::TypeError }); RefPtr page = this->page(); - if (!page || (this->document().hidden() && mode != HTMLMediaElementEnums::VideoFullscreenModeInWindow) || m_pendingFullscreenElement != element.ptr() || !element->isConnected()) + if (!page || (this->document().hidden() && mode != HTMLMediaElementEnums::VideoFullscreenModeInWindow) || !element->isConnected()) return handleError("Invalid state when requesting fullscreen."_s, EmitErrorEvent::Yes, WTFMove(completionHandler)); INFO_LOG(identifier, "task - success"); @@ -245,11 +238,7 @@ void FullscreenManager::cancelFullscreen() LOG_ONCE(SiteIsolation, "Unable to fully perform FullscreenManager::cancelFullscreen() without access to the main frame document "); if (!mainFrameDocument || !mainFrameDocument->fullscreenManager().fullscreenElement()) { - // If there is a pending fullscreen element but no top document fullscreen element, - // there is a pending task in enterFullscreen(). Cause it to cancel and fire an error - // by clearing the pending fullscreen element. - m_pendingFullscreenElement = nullptr; - INFO_LOG(LOGIDENTIFIER, "Cancelling pending fullscreen request."); + INFO_LOG(LOGIDENTIFIER, "No element to unfullscreen."); return; } @@ -355,13 +344,10 @@ void FullscreenManager::exitFullscreen(CompletionHandler) return completionHandler({ }); } - // If there is a pending fullscreen element but no fullscreen element - // there is a pending task in requestFullscreenForElement(). Cause it to cancel and fire an error - // by clearing the pending fullscreen element. + // If there is no fullscreen element, bail out early. RefPtr exitedFullscreenElement = fullscreenElement(); - if (!exitedFullscreenElement && m_pendingFullscreenElement) { - INFO_LOG(identifier, "task - Cancelling pending fullscreen request."); - m_pendingFullscreenElement = nullptr; + if (!exitedFullscreenElement) { + INFO_LOG(identifier, "task - No fullscreen element."); m_pendingExitFullscreen = false; return completionHandler({ }); } @@ -379,9 +365,8 @@ void FullscreenManager::exitFullscreen(CompletionHandler) finishExitFullscreen(*frame, ExitMode::NoResize); // We just popped off one fullscreen element out of the top layer, query the new one. - m_pendingFullscreenElement = fullscreenElement(); - if (m_pendingFullscreenElement) { - page->chrome().client().enterFullScreenForElement(*m_pendingFullscreenElement, HTMLMediaElementEnums::VideoFullscreenModeStandard, WTFMove(completionHandler), [weakThis = WTFMove(weakThis)] (bool success) { + if (RefPtr newFullscreenElement = fullscreenElement()) { + page->chrome().client().enterFullScreenForElement(*newFullscreenElement, HTMLMediaElementEnums::VideoFullscreenModeStandard, WTFMove(completionHandler), [weakThis = WTFMove(weakThis)] (bool success) { CheckedPtr checkedThis = weakThis.get(); if (!checkedThis || !success) return true; @@ -472,19 +457,6 @@ ExceptionOr FullscreenManager::willEnterFullscreen(Element& element, HTMLM return Exception { ExceptionCode::TypeError, "Cannot request fullscreen on an open popover."_s }; } - // If pending fullscreen element is unset or another element's was requested, - // issue a cancel fullscreen request to the client - if (m_pendingFullscreenElement != &element) { - INFO_LOG(LOGIDENTIFIER, "Pending element mismatch; issuing exit fullscreen request"); - page()->chrome().client().exitFullScreenForElement(&element, [weakThis = WeakPtr { *this }] { - CheckedPtr checkedThis = weakThis.get(); - if (!checkedThis) - return; - checkedThis->didExitFullscreen([] (auto) { }); - }); - return Exception { ExceptionCode::TypeError, "Element requested for fullscreen has changed."_s }; - } - INFO_LOG(LOGIDENTIFIER); ASSERT(page()->isFullscreenManagerEnabled()); @@ -495,9 +467,6 @@ ExceptionOr FullscreenManager::willEnterFullscreen(Element& element, HTMLM #endif element.willBecomeFullscreenElement(); - ASSERT(&element == m_pendingFullscreenElement); - m_pendingFullscreenElement = nullptr; - m_fullscreenElement = &element; Vector> ancestors { { element } }; @@ -553,9 +522,9 @@ bool FullscreenManager::didEnterFullscreen() bool FullscreenManager::willExitFullscreen() { - auto fullscreenElement = fullscreenOrPendingElement(); + auto fullscreenElement = m_fullscreenElement; if (!fullscreenElement) { - ERROR_LOG(LOGIDENTIFIER, "No fullscreenOrPendingElement(); bailing"); + ERROR_LOG(LOGIDENTIFIER, "No fullscreenElement, bailing"); return false; } @@ -581,13 +550,12 @@ void FullscreenManager::didExitFullscreen(CompletionHandlermainFrame(), ExitMode::Resize); - if (auto fullscreenElement = fullscreenOrPendingElement()) + if (auto fullscreenElement = m_fullscreenElement) fullscreenElement->didStopBeingFullscreenElement(); m_areKeysEnabledInFullscreen = false; m_fullscreenElement = nullptr; - m_pendingFullscreenElement = nullptr; m_pendingExitFullscreen = false; completionHandler({ }); @@ -684,7 +652,6 @@ void FullscreenManager::setAnimatingFullscreen(bool flag) void FullscreenManager::clear() { m_fullscreenElement = nullptr; - m_pendingFullscreenElement = nullptr; } void FullscreenManager::emptyEventQueue() diff --git a/Source/WebCore/dom/FullscreenManager.h b/Source/WebCore/dom/FullscreenManager.h index 84405d7a3a86a..bc6ba8afcbe8e 100644 --- a/Source/WebCore/dom/FullscreenManager.h +++ b/Source/WebCore/dom/FullscreenManager.h @@ -110,14 +110,11 @@ class FullscreenManager final : public CanMakeWeakPtr, public Document* mainFrameDocument() { return document().mainFrameDocument(); } RefPtr protectedMainFrameDocument() { return mainFrameDocument(); } - RefPtr fullscreenOrPendingElement() const { return m_fullscreenElement ? m_fullscreenElement : m_pendingFullscreenElement; } - bool didEnterFullscreen(); void addElementToChangeEventQueue(Node& target) { m_fullscreenChangeEventTargetQueue.append(GCReachableRef(target)); } WeakRef m_document; - RefPtr m_pendingFullscreenElement; RefPtr m_fullscreenElement; Deque> m_fullscreenChangeEventTargetQueue; From 88dc8b0d15913d5bf57441661d52c085ba39b520 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Fri, 21 Feb 2025 21:29:08 -0800 Subject: [PATCH 035/174] Address safer CPP failures in DeviceIdHashSaltStorage.cpp https://bugs.webkit.org/show_bug.cgi?id=288237 Reviewed by Ryosuke Niwa. * Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations: * Source/WebKit/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations: * Source/WebKit/UIProcess/DeviceIdHashSaltStorage.cpp: (WebKit::DeviceIdHashSaltStorage::deviceIdHashSaltForOrigin): (WebKit::DeviceIdHashSaltStorage::getDeviceIdHashSaltOrigins): (WebKit::DeviceIdHashSaltStorage::deleteDeviceIdHashSaltForOrigins): (WebKit::DeviceIdHashSaltStorage::deleteDeviceIdHashSaltOriginsModifiedSince): * Source/WebKit/UIProcess/DeviceIdHashSaltStorage.h: Canonical link: https://commits.webkit.org/290856@main --- .../UncountedCallArgsCheckerExpectations | 1 - ...UncountedLambdaCapturesCheckerExpectations | 1 - .../UIProcess/DeviceIdHashSaltStorage.cpp | 30 ++++++++++++------- .../UIProcess/DeviceIdHashSaltStorage.h | 5 ++-- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations b/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations index 97d81ad726d2e..3dc7387f7711b 100644 --- a/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations +++ b/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations @@ -175,7 +175,6 @@ UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.mm UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.mm UIProcess/Cocoa/UserMediaPermissionRequestManagerProxy.mm UIProcess/Cocoa/WKEditCommand.mm -UIProcess/DeviceIdHashSaltStorage.cpp UIProcess/Extensions/Cocoa/WebExtensionActionCocoa.mm UIProcess/Extensions/Cocoa/WebExtensionCommandCocoa.mm UIProcess/Extensions/Cocoa/WebExtensionURLSchemeHandlerCocoa.mm diff --git a/Source/WebKit/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations b/Source/WebKit/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations index 226c882aaacd6..c084aa525c19d 100644 --- a/Source/WebKit/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations +++ b/Source/WebKit/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations @@ -5,7 +5,6 @@ Platform/cocoa/WebPrivacyHelpers.mm UIProcess/Automation/SimulatedInputDispatcher.cpp UIProcess/Automation/WebAutomationSession.cpp UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.mm -UIProcess/DeviceIdHashSaltStorage.cpp UIProcess/Extensions/Cocoa/WebExtensionURLSchemeHandlerCocoa.mm UIProcess/SpeechRecognitionPermissionManager.cpp UIProcess/UserMediaPermissionRequestManagerProxy.cpp diff --git a/Source/WebKit/UIProcess/DeviceIdHashSaltStorage.cpp b/Source/WebKit/UIProcess/DeviceIdHashSaltStorage.cpp index d2a9bbe13cc09..f930d6d60b312 100644 --- a/Source/WebKit/UIProcess/DeviceIdHashSaltStorage.cpp +++ b/Source/WebKit/UIProcess/DeviceIdHashSaltStorage.cpp @@ -235,8 +235,9 @@ void DeviceIdHashSaltStorage::deviceIdHashSaltForOrigin(const SecurityOrigin& do ASSERT(RunLoop::isMain()); if (!m_isLoaded) { - m_pendingCompletionHandlers.append([this, documentOrigin = documentOrigin.data().isolatedCopy(), parentOrigin = parentOrigin.data().isolatedCopy(), completionHandler = WTFMove(completionHandler)]() mutable { - completeDeviceIdHashSaltForOriginCall(WTFMove(documentOrigin), WTFMove(parentOrigin), WTFMove(completionHandler)); + m_pendingCompletionHandlers.append([weakThis = ThreadSafeWeakPtr { *this }, documentOrigin = documentOrigin.data().isolatedCopy(), parentOrigin = parentOrigin.data().isolatedCopy(), completionHandler = WTFMove(completionHandler)]() mutable { + if (RefPtr protectedThis = weakThis.get()) + protectedThis->completeDeviceIdHashSaltForOriginCall(WTFMove(documentOrigin), WTFMove(parentOrigin), WTFMove(completionHandler)); }); return; } @@ -249,8 +250,9 @@ void DeviceIdHashSaltStorage::getDeviceIdHashSaltOrigins(CompletionHandlercompletePendingHandler(WTFMove(completionHandler)); }); return; } @@ -273,11 +275,15 @@ void DeviceIdHashSaltStorage::deleteDeviceIdHashSaltForOrigins(const Vectorm_isClosed) return completionHandler(); - deleteDeviceIdHashSaltForOrigins(origins, WTFMove(completionHandler)); + protectedThis->deleteDeviceIdHashSaltForOrigins(origins, WTFMove(completionHandler)); }); return; } @@ -299,11 +305,15 @@ void DeviceIdHashSaltStorage::deleteDeviceIdHashSaltOriginsModifiedSince(WallTim ASSERT(RunLoop::isMain()); if (!m_isLoaded) { - m_pendingCompletionHandlers.append([this, time, completionHandler = WTFMove(completionHandler)]() mutable { - if (m_isClosed) + m_pendingCompletionHandlers.append([weakThis = ThreadSafeWeakPtr { *this }, time, completionHandler = WTFMove(completionHandler)]() mutable { + RefPtr protectedThis = weakThis.get(); + if (!protectedThis) + return; + + if (protectedThis->m_isClosed) return completionHandler(); - deleteDeviceIdHashSaltOriginsModifiedSince(time, WTFMove(completionHandler)); + protectedThis->deleteDeviceIdHashSaltOriginsModifiedSince(time, WTFMove(completionHandler)); }); return; } diff --git a/Source/WebKit/UIProcess/DeviceIdHashSaltStorage.h b/Source/WebKit/UIProcess/DeviceIdHashSaltStorage.h index ac42483c65b54..8aec6f2aa975a 100644 --- a/Source/WebKit/UIProcess/DeviceIdHashSaltStorage.h +++ b/Source/WebKit/UIProcess/DeviceIdHashSaltStorage.h @@ -32,11 +32,12 @@ #include #include #include +#include #include namespace WebKit { -class DeviceIdHashSaltStorage : public ThreadSafeRefCounted { +class DeviceIdHashSaltStorage : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr { public: static Ref create(const String& deviceIdHashSaltStorageDirectory); ~DeviceIdHashSaltStorage(); @@ -76,7 +77,7 @@ class DeviceIdHashSaltStorage : public ThreadSafeRefCounted&&)>&&); void completeDeviceIdHashSaltForOriginCall(WebCore::SecurityOriginData&& documentOrigin, WebCore::SecurityOriginData&& parentOrigin, CompletionHandler&&); - Ref m_queue; + const Ref m_queue; HashMap> m_deviceIdHashSaltForOrigins; bool m_isLoaded { false }; bool m_isClosed { false }; From 6a1bea9490ae15e546b0caaf83ba65857c88da05 Mon Sep 17 00:00:00 2001 From: Alex Christensen Date: Fri, 21 Feb 2025 21:33:30 -0800 Subject: [PATCH 036/174] Fix TestWebKitAPI.WKDownload.DownloadRequestFailure after 289946@main https://bugs.webkit.org/show_bug.cgi?id=288259 rdar://145330588 Reviewed by Ryosuke Niwa. "ftp:///" is a URL that NSURL thinks is valid but WTF::URL thinks is invalid. Feeding such a URL into WKWebView.startDownloadUsingRequest shouldn't crash. It should fail gracefully like it used to. * Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm: (WebKit::NetworkDataTaskCocoa::NetworkDataTaskCocoa): Canonical link: https://commits.webkit.org/290857@main --- Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm b/Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm index 9af3d5b51bf40..f07ba92479bd0 100644 --- a/Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm +++ b/Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm @@ -194,6 +194,11 @@ static float toNSURLSessionTaskPriority(WebCore::ResourceLoadPriority priority) { auto request = parameters.request; auto url = request.url(); + if (url.isNull()) { + scheduleFailure(FailureType::InvalidURL); + return; + } + if (m_storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use && url.protocolIsInHTTPFamily()) { m_user = url.user(); m_password = url.password(); From b089d5f9e8e6d7966d4094f23a437b4bbe7de0f9 Mon Sep 17 00:00:00 2001 From: Alex Christensen Date: Fri, 21 Feb 2025 22:13:46 -0800 Subject: [PATCH 037/174] Fix some layout tests after 290596@main https://bugs.webkit.org/show_bug.cgi?id=288282 Unreviewed. * LayoutTests/http/tests/dom/noreferrer-window-not-targetable-expected.txt: * LayoutTests/http/tests/dom/noreferrer-window-not-targetable.html: * LayoutTests/http/tests/download/sandboxed-iframe-download-not-allowed-expected.txt: Canonical link: https://commits.webkit.org/290858@main --- .../tests/dom/noreferrer-window-not-targetable-expected.txt | 2 +- .../http/tests/dom/noreferrer-window-not-targetable.html | 6 +----- .../sandboxed-iframe-download-not-allowed-expected.txt | 2 -- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/LayoutTests/http/tests/dom/noreferrer-window-not-targetable-expected.txt b/LayoutTests/http/tests/dom/noreferrer-window-not-targetable-expected.txt index b69451ffbfc69..9ebeb73aeb2f7 100644 --- a/LayoutTests/http/tests/dom/noreferrer-window-not-targetable-expected.txt +++ b/LayoutTests/http/tests/dom/noreferrer-window-not-targetable-expected.txt @@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE PASS w.location.href is "about:blank" -PASS testRunner.windowCount() is 2 +PASS testRunner.windowCount() is 3 PASS successfullyParsed is true TEST COMPLETE diff --git a/LayoutTests/http/tests/dom/noreferrer-window-not-targetable.html b/LayoutTests/http/tests/dom/noreferrer-window-not-targetable.html index 6a005e9f58530..c18e92711ac83 100644 --- a/LayoutTests/http/tests/dom/noreferrer-window-not-targetable.html +++ b/LayoutTests/http/tests/dom/noreferrer-window-not-targetable.html @@ -16,11 +16,7 @@ shouldBeEqualToString("w.location.href", "about:blank"); w.onload = function() { if (window.testRunner) { - if (testRunner.isWebKit2) { - shouldBe("testRunner.windowCount()", "2"); - } else { - shouldBe("testRunner.windowCount()", "3"); - } + shouldBe("testRunner.windowCount()", "3"); } finishJSTest(); } diff --git a/LayoutTests/http/tests/download/sandboxed-iframe-download-not-allowed-expected.txt b/LayoutTests/http/tests/download/sandboxed-iframe-download-not-allowed-expected.txt index 8b85242e322b0..298b234dfd829 100644 --- a/LayoutTests/http/tests/download/sandboxed-iframe-download-not-allowed-expected.txt +++ b/LayoutTests/http/tests/download/sandboxed-iframe-download-not-allowed-expected.txt @@ -1,7 +1,5 @@ CONSOLE MESSAGE: Not allowed to download due to sandboxing CONSOLE MESSAGE: Not allowed to download due to sandboxing -CONSOLE MESSAGE: Not allowed to download due to sandboxing -CONSOLE MESSAGE: Not allowed to download due to sandboxing This test passes if no download is started. On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". From a3dded6670b8b8cfda8658107a6dc97e6754753c Mon Sep 17 00:00:00 2001 From: Alex Christensen Date: Fri, 21 Feb 2025 22:38:42 -0800 Subject: [PATCH 038/174] Always call completion handlers in FullscreenManager::exitFullscreen https://bugs.webkit.org/show_bug.cgi?id=288276 rdar://145343638 Reviewed by Tim Nguyen. I made a mistake, resulting in timing-dependent assertions when run loops terminate. This introduces CompletionHandlerScope to call the CompletionHandler if it goes out of scope when the queueTask call is never queued. * Source/WebCore/dom/FullscreenManager.cpp: (WebCore::FullscreenManager::exitFullscreen): Canonical link: https://commits.webkit.org/290859@main --- Source/WebCore/dom/FullscreenManager.cpp | 25 +++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/Source/WebCore/dom/FullscreenManager.cpp b/Source/WebCore/dom/FullscreenManager.cpp index d4f550f73715b..f6c0daff57cdf 100644 --- a/Source/WebCore/dom/FullscreenManager.cpp +++ b/Source/WebCore/dom/FullscreenManager.cpp @@ -85,6 +85,22 @@ Element* FullscreenManager::fullscreenElement() const return nullptr; } +class CompletionHandlerScope final { +public: + CompletionHandlerScope(CompletionHandler)>&& completionHandler) + : m_completionHandler(WTFMove(completionHandler)) { } + CompletionHandlerScope(CompletionHandlerScope&&) = default; + CompletionHandlerScope& operator=(CompletionHandlerScope&&) = default; + ~CompletionHandlerScope() + { + if (m_completionHandler) + m_completionHandler({ }); + } + CompletionHandler)> release() { return WTFMove(m_completionHandler); } +private: + CompletionHandler)> m_completionHandler; +}; + // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen void FullscreenManager::requestFullscreenForElement(Ref&& element, FullscreenCheckType checkType, CompletionHandler)>&& completionHandler, HTMLMediaElementEnums::VideoFullscreenMode mode) { @@ -165,7 +181,8 @@ void FullscreenManager::requestFullscreenForElement(Ref&& element, Full INFO_LOG(identifier); - protectedDocument()->eventLoop().queueTask(TaskSource::MediaElement, [this, weakThis = WeakPtr { *this }, element = WTFMove(element), completionHandler = WTFMove(completionHandler), hasKeyboardAccess, fullscreenElementReadyCheck, handleError, identifier, mode] () mutable { + protectedDocument()->eventLoop().queueTask(TaskSource::MediaElement, [this, weakThis = WeakPtr { *this }, element = WTFMove(element), scope = CompletionHandlerScope(WTFMove(completionHandler)), hasKeyboardAccess, fullscreenElementReadyCheck, handleError, identifier, mode] () mutable { + auto completionHandler = scope.release(); CheckedPtr checkedThis = weakThis.get(); if (!checkedThis) return completionHandler(Exception { ExceptionCode::TypeError }); @@ -204,7 +221,8 @@ void FullscreenManager::requestFullscreenForElement(Ref&& element, Full // 5. Return, and run the remaining steps asynchronously. // 6. Optionally, perform some animation. m_areKeysEnabledInFullscreen = hasKeyboardAccess; - document->eventLoop().queueTask(TaskSource::MediaElement, [this, weakThis = WTFMove(weakThis), element = WTFMove(element), completionHandler = WTFMove(completionHandler), handleError = WTFMove(handleError), identifier, mode] () mutable { + document->eventLoop().queueTask(TaskSource::MediaElement, [this, weakThis = WTFMove(weakThis), element = WTFMove(element), scope = CompletionHandlerScope(WTFMove(completionHandler)), handleError = WTFMove(handleError), identifier, mode] () mutable { + auto completionHandler = scope.release(); CheckedPtr checkedThis = weakThis.get(); if (!checkedThis) return completionHandler(Exception { ExceptionCode::TypeError }); @@ -332,7 +350,8 @@ void FullscreenManager::exitFullscreen(CompletionHandler) m_pendingExitFullscreen = true; // Return promise, and run the remaining steps in parallel. - exitingDocument->eventLoop().queueTask(TaskSource::MediaElement, [this, completionHandler = WTFMove(completionHandler), weakThis = WeakPtr { *this }, mode, identifier = LOGIDENTIFIER] () mutable { + exitingDocument->eventLoop().queueTask(TaskSource::MediaElement, [this, scope = CompletionHandlerScope(WTFMove(completionHandler)), weakThis = WeakPtr { *this }, mode, identifier = LOGIDENTIFIER] () mutable { + auto completionHandler = scope.release(); CheckedPtr checkedThis = weakThis.get(); if (!checkedThis) return completionHandler({ }); From 6dd55289e6362820166b2296b07f6f3cbef97946 Mon Sep 17 00:00:00 2001 From: Alex Christensen Date: Fri, 21 Feb 2025 22:40:43 -0800 Subject: [PATCH 039/174] Fix WKDownload.originatingFrame of downloads originated without a frame https://bugs.webkit.org/show_bug.cgi?id=288253 rdar://145328556 Reviewed by Chris Dumez. WKDownload.originatingFrame is a non-nullable property, and it has non-nullable properties WKFrameInfo.request and securityOrigin. We were returning null for those properties, which could cause crashes in Swift. This will fix those crashes by making a fake WKFrameInfo that represents a frame that doesn't exist that has loaded about:blank. * Source/WebKit/UIProcess/API/APINavigation.h: (API::Navigation::originatingFrameInfo const): * Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp: (WebKit::legacyEmptyFrameInfo): (WebKit::DownloadProxy::DownloadProxy): * Source/WebKit/UIProcess/Downloads/DownloadProxy.h: * Source/WebKit/UIProcess/Downloads/DownloadProxyMap.cpp: (WebKit::DownloadProxyMap::createDownloadProxy): * Source/WebKit/UIProcess/Downloads/DownloadProxyMap.h: * Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp: (WebKit::NetworkProcessProxy::createDownloadProxy): * Source/WebKit/UIProcess/Network/NetworkProcessProxy.h: * Source/WebKit/UIProcess/WebPageProxy.cpp: (WebKit::WebPageProxy::receivedPolicyDecision): (WebKit::WebPageProxy::receivedNavigationResponsePolicyDecision): * Source/WebKit/UIProcess/WebProcessPool.cpp: (WebKit::WebProcessPool::download): (WebKit::WebProcessPool::createDownloadProxy): * Source/WebKit/UIProcess/WebProcessPool.h: * Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp: (WebKit::WebsiteDataStore::createDownloadProxy): * Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h: * Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm: (TestWebKitAPI::OriginatingFrameAndUserGesture)): Canonical link: https://commits.webkit.org/290860@main --- Source/WebKit/UIProcess/API/APINavigation.h | 4 +-- .../UIProcess/Downloads/DownloadProxy.cpp | 27 +++++++++++++++++-- .../UIProcess/Downloads/DownloadProxy.h | 2 +- .../UIProcess/Downloads/DownloadProxyMap.cpp | 2 +- .../UIProcess/Downloads/DownloadProxyMap.h | 2 +- .../UIProcess/Network/NetworkProcessProxy.cpp | 2 +- .../UIProcess/Network/NetworkProcessProxy.h | 2 +- Source/WebKit/UIProcess/WebPageProxy.cpp | 4 +-- Source/WebKit/UIProcess/WebProcessPool.cpp | 4 +-- Source/WebKit/UIProcess/WebProcessPool.h | 4 +-- .../WebsiteData/WebsiteDataStore.cpp | 2 +- .../UIProcess/WebsiteData/WebsiteDataStore.h | 2 +- .../Tests/WebKitCocoa/Download.mm | 4 ++- 13 files changed, 43 insertions(+), 18 deletions(-) diff --git a/Source/WebKit/UIProcess/API/APINavigation.h b/Source/WebKit/UIProcess/API/APINavigation.h index a39ef2ee49596..a45bbcf60f9f9 100644 --- a/Source/WebKit/UIProcess/API/APINavigation.h +++ b/Source/WebKit/UIProcess/API/APINavigation.h @@ -147,7 +147,7 @@ class Navigation : public ObjectImpl { const WebKit::NavigationActionData& lastNavigationAction() const { return m_lastNavigationAction; } void setOriginatingFrameInfo(const WebKit::FrameInfoData& frameInfo) { m_originatingFrameInfo = frameInfo; } - const WebKit::FrameInfoData& originatingFrameInfo() const { return m_originatingFrameInfo; } + const std::optional& originatingFrameInfo() const { return m_originatingFrameInfo; } void setDestinationFrameSecurityOrigin(const WebCore::SecurityOriginData& origin) { m_destinationFrameSecurityOrigin = origin; } const WebCore::SecurityOriginData& destinationFrameSecurityOrigin() const { return m_destinationFrameSecurityOrigin; } @@ -199,7 +199,7 @@ class Navigation : public ObjectImpl { std::optional m_backForwardFrameLoadType; std::unique_ptr m_substituteData; WebKit::NavigationActionData m_lastNavigationAction; - WebKit::FrameInfoData m_originatingFrameInfo; + std::optional m_originatingFrameInfo; WebCore::SecurityOriginData m_destinationFrameSecurityOrigin; bool m_userContentExtensionsEnabled { true }; WebKit::WebContentMode m_effectiveContentMode { WebKit::WebContentMode::Recommended }; diff --git a/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp b/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp index 8fe224d8c374d..5d0385c213e49 100644 --- a/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp +++ b/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp @@ -55,14 +55,37 @@ namespace WebKit { using namespace WebCore; -DownloadProxy::DownloadProxy(DownloadProxyMap& downloadProxyMap, WebsiteDataStore& dataStore, API::DownloadClient& client, const ResourceRequest& resourceRequest, const FrameInfoData& frameInfoData, WebPageProxy* originatingPage) +static FrameInfoData legacyEmptyFrameInfo() +{ + constexpr bool isMainFrame { false }; + constexpr bool isFocused { false }; + constexpr bool errorOccurred { false }; + + return FrameInfoData { + isMainFrame, + FrameType::Local, + ResourceRequest { aboutBlankURL() }, + SecurityOriginData::createOpaque(), + String { }, + FrameIdentifier::generate(), + std::nullopt, + std::nullopt, + CertificateInfo { }, + getCurrentProcessID(), + isFocused, + errorOccurred, + WebFrameMetrics { } + }; +} + +DownloadProxy::DownloadProxy(DownloadProxyMap& downloadProxyMap, WebsiteDataStore& dataStore, API::DownloadClient& client, const ResourceRequest& resourceRequest, const std::optional& frameInfoData, WebPageProxy* originatingPage) : m_downloadProxyMap(downloadProxyMap) , m_dataStore(&dataStore) , m_client(client) , m_downloadID(DownloadID::generate()) , m_request(resourceRequest) , m_originatingPage(originatingPage) - , m_frameInfo(API::FrameInfo::create(FrameInfoData { frameInfoData }, originatingPage)) + , m_frameInfo(frameInfoData ? API::FrameInfo::create(FrameInfoData { *frameInfoData }, originatingPage) : API::FrameInfo::create(legacyEmptyFrameInfo(), originatingPage)) #if HAVE(MODERN_DOWNLOADPROGRESS) , m_assertion(ProcessAssertion::create(getCurrentProcessID(), "WebKit DownloadProxy DecideDestination"_s, ProcessAssertionType::FinishTaskInterruptable)) #endif diff --git a/Source/WebKit/UIProcess/Downloads/DownloadProxy.h b/Source/WebKit/UIProcess/Downloads/DownloadProxy.h index 5c48a4c5f1747..d82b401d7d58a 100644 --- a/Source/WebKit/UIProcess/Downloads/DownloadProxy.h +++ b/Source/WebKit/UIProcess/Downloads/DownloadProxy.h @@ -131,7 +131,7 @@ class DownloadProxy : public API::ObjectImpl, publi void decideDestinationWithSuggestedFilename(const WebCore::ResourceResponse&, String&& suggestedFilename, DecideDestinationCallback&&); private: - explicit DownloadProxy(DownloadProxyMap&, WebsiteDataStore&, API::DownloadClient&, const WebCore::ResourceRequest&, const FrameInfoData&, WebPageProxy*); + explicit DownloadProxy(DownloadProxyMap&, WebsiteDataStore&, API::DownloadClient&, const WebCore::ResourceRequest&, const std::optional&, WebPageProxy*); Ref protectedClient() const; RefPtr protectedDataStore() { return m_dataStore; } diff --git a/Source/WebKit/UIProcess/Downloads/DownloadProxyMap.cpp b/Source/WebKit/UIProcess/Downloads/DownloadProxyMap.cpp index 67493062fdb43..272bcb3f827ea 100644 --- a/Source/WebKit/UIProcess/Downloads/DownloadProxyMap.cpp +++ b/Source/WebKit/UIProcess/Downloads/DownloadProxyMap.cpp @@ -87,7 +87,7 @@ void DownloadProxyMap::platformDestroy() } #endif -Ref DownloadProxyMap::createDownloadProxy(WebsiteDataStore& dataStore, Ref&& client, const WebCore::ResourceRequest& resourceRequest, const FrameInfoData& frameInfo, WebPageProxy* originatingPage) +Ref DownloadProxyMap::createDownloadProxy(WebsiteDataStore& dataStore, Ref&& client, const WebCore::ResourceRequest& resourceRequest, const std::optional& frameInfo, WebPageProxy* originatingPage) { auto downloadProxy = DownloadProxy::create(*this, dataStore, WTFMove(client), resourceRequest, frameInfo, originatingPage); m_downloads.set(downloadProxy->downloadID(), downloadProxy.copyRef()); diff --git a/Source/WebKit/UIProcess/Downloads/DownloadProxyMap.h b/Source/WebKit/UIProcess/Downloads/DownloadProxyMap.h index 6bed156c5e20d..a9de081d2478f 100644 --- a/Source/WebKit/UIProcess/Downloads/DownloadProxyMap.h +++ b/Source/WebKit/UIProcess/Downloads/DownloadProxyMap.h @@ -60,7 +60,7 @@ class DownloadProxyMap : public CanMakeWeakPtr { explicit DownloadProxyMap(NetworkProcessProxy&); ~DownloadProxyMap(); - Ref createDownloadProxy(WebsiteDataStore&, Ref&&, const WebCore::ResourceRequest&, const FrameInfoData&, WebPageProxy* originatingPage); + Ref createDownloadProxy(WebsiteDataStore&, Ref&&, const WebCore::ResourceRequest&, const std::optional&, WebPageProxy* originatingPage); void downloadFinished(DownloadProxy&); bool isEmpty() const { return m_downloads.isEmpty(); } diff --git a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp index e952de7456d82..2adc88c448427 100644 --- a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp +++ b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp @@ -361,7 +361,7 @@ void NetworkProcessProxy::synthesizeAppIsBackground(bool background) applicationWillEnterForeground(); } -Ref NetworkProcessProxy::createDownloadProxy(WebsiteDataStore& dataStore, Ref&& client, const ResourceRequest& resourceRequest, const FrameInfoData& frameInfo, WebPageProxy* originatingPage) +Ref NetworkProcessProxy::createDownloadProxy(WebsiteDataStore& dataStore, Ref&& client, const ResourceRequest& resourceRequest, const std::optional& frameInfo, WebPageProxy* originatingPage) { if (!m_downloadProxyMap) m_downloadProxyMap = makeUniqueWithoutRefCountedCheck(*this); diff --git a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h index fa79d399917eb..4857537c69a60 100644 --- a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h +++ b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h @@ -137,7 +137,7 @@ class NetworkProcessProxy final : public AuxiliaryProcessProxy { void sharedPreferencesForWebProcessDidChange(WebProcessProxy&, SharedPreferencesForWebProcess&&, CompletionHandler&&); - Ref createDownloadProxy(WebsiteDataStore&, Ref&&, const WebCore::ResourceRequest&, const FrameInfoData&, WebPageProxy* originatingPage); + Ref createDownloadProxy(WebsiteDataStore&, Ref&&, const WebCore::ResourceRequest&, const std::optional&, WebPageProxy* originatingPage); void dataTaskWithRequest(WebPageProxy&, PAL::SessionID, WebCore::ResourceRequest&&, const std::optional& topOrigin, bool shouldRunAtForegroundPriority, CompletionHandler&&); void addAllowedFirstPartyForCookies(WebProcessProxy&, const WebCore::RegistrableDomain& firstPartyForCookies, LoadedWebArchive, CompletionHandler&&); diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp index e31ed810cb6dd..dc56bbddb5e15 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.cpp +++ b/Source/WebKit/UIProcess/WebPageProxy.cpp @@ -4987,7 +4987,7 @@ void WebPageProxy::receivedPolicyDecision(PolicyAction action, API::Navigation* std::optional downloadID; if (action == PolicyAction::Download) { // Create a download proxy. - auto download = m_legacyMainFrameProcess->protectedProcessPool()->createDownloadProxy(m_websiteDataStore, navigationAction->request(), this, navigation ? navigation->originatingFrameInfo() : FrameInfoData { }); + auto download = m_legacyMainFrameProcess->protectedProcessPool()->createDownloadProxy(m_websiteDataStore, navigationAction->request(), this, navigation ? std::optional(navigation->originatingFrameInfo()) : std::nullopt); download->setDidStartCallback([weakThis = WeakPtr { *this }, navigationAction = WTFMove(navigationAction)] (auto* downloadProxy) { RefPtr protectedThis = weakThis.get(); if (!protectedThis || !downloadProxy) @@ -5024,7 +5024,7 @@ void WebPageProxy::receivedNavigationResponsePolicyDecision(WebCore::PolicyActio std::optional downloadID; if (action == PolicyAction::Download) { - auto download = m_legacyMainFrameProcess->protectedProcessPool()->createDownloadProxy(m_websiteDataStore, request, this, navigation ? navigation->originatingFrameInfo() : FrameInfoData { }); + auto download = m_legacyMainFrameProcess->protectedProcessPool()->createDownloadProxy(m_websiteDataStore, request, this, navigation ? std::optional(navigation->originatingFrameInfo()) : std::nullopt); download->setDidStartCallback([weakThis = WeakPtr { *this }, navigationResponse = WTFMove(navigationResponse)] (auto* downloadProxy) { RefPtr protectedThis = weakThis.get(); if (!protectedThis || !downloadProxy) diff --git a/Source/WebKit/UIProcess/WebProcessPool.cpp b/Source/WebKit/UIProcess/WebProcessPool.cpp index 17c3999389c9d..c035cc55b3810 100644 --- a/Source/WebKit/UIProcess/WebProcessPool.cpp +++ b/Source/WebKit/UIProcess/WebProcessPool.cpp @@ -1346,7 +1346,7 @@ bool WebProcessPool::hasPagesUsingWebsiteDataStore(WebsiteDataStore& dataStore) return m_sessionToPageIDsMap.contains(dataStore.sessionID()); } -Ref WebProcessPool::download(WebsiteDataStore& dataStore, WebPageProxy* initiatingPage, const ResourceRequest& request, const FrameInfoData& frameInfo, const String& suggestedFilename) +Ref WebProcessPool::download(WebsiteDataStore& dataStore, WebPageProxy* initiatingPage, const ResourceRequest& request, const std::optional& frameInfo, const String& suggestedFilename) { Ref downloadProxy = createDownloadProxy(dataStore, request, initiatingPage, frameInfo); dataStore.download(downloadProxy, suggestedFilename); @@ -1569,7 +1569,7 @@ void WebProcessPool::setDefaultRequestTimeoutInterval(double timeoutInterval) sendToAllProcesses(Messages::WebProcess::SetDefaultRequestTimeoutInterval(timeoutInterval)); } -Ref WebProcessPool::createDownloadProxy(WebsiteDataStore& dataStore, const ResourceRequest& request, WebPageProxy* originatingPage, const FrameInfoData& frameInfo) +Ref WebProcessPool::createDownloadProxy(WebsiteDataStore& dataStore, const ResourceRequest& request, WebPageProxy* originatingPage, const std::optional& frameInfo) { Ref client = m_legacyDownloadClient ? Ref(*m_legacyDownloadClient) : adoptRef(*new API::DownloadClient); return dataStore.createDownloadProxy(WTFMove(client), request, originatingPage, frameInfo); diff --git a/Source/WebKit/UIProcess/WebProcessPool.h b/Source/WebKit/UIProcess/WebProcessPool.h index 8efef86f9fd7a..5e6648e40d7b5 100644 --- a/Source/WebKit/UIProcess/WebProcessPool.h +++ b/Source/WebKit/UIProcess/WebProcessPool.h @@ -251,7 +251,7 @@ class WebProcessPool final const String& injectedBundlePath() const { return m_configuration->injectedBundlePath(); } - Ref download(WebsiteDataStore&, WebPageProxy* initiatingPage, const WebCore::ResourceRequest&, const FrameInfoData&, const String& suggestedFilename = { }); + Ref download(WebsiteDataStore&, WebPageProxy* initiatingPage, const WebCore::ResourceRequest&, const std::optional&, const String& suggestedFilename = { }); Ref resumeDownload(WebsiteDataStore&, WebPageProxy* initiatingPage, const API::Data& resumeData, const String& path, CallDownloadDidStart); void setInjectedBundleInitializationUserData(RefPtr&& userData) { m_injectedBundleInitializationUserData = WTFMove(userData); } @@ -319,7 +319,7 @@ class WebProcessPool final void setEnhancedAccessibility(bool); // Downloads. - Ref createDownloadProxy(WebsiteDataStore&, const WebCore::ResourceRequest&, WebPageProxy* originatingPage, const FrameInfoData&); + Ref createDownloadProxy(WebsiteDataStore&, const WebCore::ResourceRequest&, WebPageProxy* originatingPage, const std::optional&); API::LegacyContextHistoryClient& historyClient() { return *m_historyClient; } WebContextClient& client() { return m_client; } diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp index 5f08b9c25e998..460b0cafbe6d8 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp @@ -2683,7 +2683,7 @@ void WebsiteDataStore::setEmulatedConditions(std::optional&& bytesPerSe #endif // ENABLE(INSPECTOR_NETWORK_THROTTLING) -Ref WebsiteDataStore::createDownloadProxy(Ref&& client, const WebCore::ResourceRequest& request, WebPageProxy* originatingPage, const FrameInfoData& frameInfo) +Ref WebsiteDataStore::createDownloadProxy(Ref&& client, const WebCore::ResourceRequest& request, WebPageProxy* originatingPage, const std::optional& frameInfo) { return protectedNetworkProcess()->createDownloadProxy(*this, WTFMove(client), request, frameInfo, originatingPage); } diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h index fe994b5195e84..b495136083818 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h @@ -475,7 +475,7 @@ class WebsiteDataStore : public API::ObjectImpl createDownloadProxy(Ref&&, const WebCore::ResourceRequest&, WebPageProxy* originatingPage, const FrameInfoData&); + Ref createDownloadProxy(Ref&&, const WebCore::ResourceRequest&, WebPageProxy* originatingPage, const std::optional&); void download(const DownloadProxy&, const String& suggestedFilename); void resumeDownload(const DownloadProxy&, const API::Data&, const String& path, CallDownloadDidStart); diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm index 7e6f2a273bb87..9539dab03a136 100644 --- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm +++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm @@ -3180,8 +3180,10 @@ HTTPServer childFrameServer({ auto emptyWebView = adoptNS([[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration]); checkedDownload = false; [emptyWebView startDownloadUsingRequest:request completionHandler:^(WKDownload *download) { + EXPECT_NOT_NULL(download.originatingFrame.securityOrigin); EXPECT_WK_STREQ(download.originatingFrame.securityOrigin.host, ""); - EXPECT_WK_STREQ(download.originatingFrame.request.URL.absoluteString, ""); + EXPECT_NOT_NULL(download.originatingFrame.request); + EXPECT_WK_STREQ(download.originatingFrame.request.URL.absoluteString, "about:blank"); EXPECT_TRUE(download.isUserInitiated); checkedDownload = true; }]; From b0cdfcd9114c169203b45c79416cbdfc58a667da Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Fri, 21 Feb 2025 23:02:03 -0800 Subject: [PATCH 040/174] Address safer CPP failures in MediaSampleAVFObjC.mm https://bugs.webkit.org/show_bug.cgi?id=288229 Reviewed by Ryosuke Niwa. * Source/WebCore/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations: * Source/WebCore/platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm: (WebCore::MediaSampleAVFObjC::divide): Canonical link: https://commits.webkit.org/290861@main --- .../UncountedLambdaCapturesCheckerExpectations | 1 - .../platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/WebCore/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations index 444df974d18c8..e9c5d4c2fc276 100644 --- a/Source/WebCore/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations @@ -139,7 +139,6 @@ platform/encryptedmedia/clearkey/CDMClearKey.cpp platform/graphics/avfoundation/InbandTextTrackPrivateAVF.cpp platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm platform/graphics/avfoundation/objc/InbandChapterTrackPrivateAVFObjC.mm -platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm platform/graphics/cocoa/VideoMediaSampleRenderer.mm platform/graphics/filters/FilterOperation.cpp platform/mediarecorder/MediaRecorderPrivateEncoder.cpp diff --git a/Source/WebCore/platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm b/Source/WebCore/platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm index 7a12d600a65e5..234af467dadba 100644 --- a/Source/WebCore/platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm +++ b/Source/WebCore/platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm @@ -297,8 +297,8 @@ static bool isCMSampleBufferAttachmentNonDisplaying(CFDictionaryRef attachmentDi Vector> samples; samples.reserveInitialCapacity(numSamples); - PAL::CMSampleBufferCallBlockForEachSample(m_sample.get(), [&] (CMSampleBufferRef sampleBuffer, CMItemCount) -> OSStatus { - samples.append(MediaSampleAVFObjC::create(sampleBuffer, m_id)); + PAL::CMSampleBufferCallBlockForEachSample(m_sample.get(), [&samples, id = m_id] (CMSampleBufferRef sampleBuffer, CMItemCount) -> OSStatus { + samples.append(MediaSampleAVFObjC::create(sampleBuffer, id)); return noErr; }); return samples; From e2fd0631b1c4c99f3cbccfe0c8c88aa7c4b6ec09 Mon Sep 17 00:00:00 2001 From: Joshua Hoffman Date: Fri, 21 Feb 2025 23:03:15 -0800 Subject: [PATCH 041/174] AX: [AX-Thread Text APIs] Allow partialOrderByTraversal to return 'unordered' https://bugs.webkit.org/show_bug.cgi?id=288214 rdar://145305918 Reviewed by Tyler Wilcock. This PR downgrades the release assert in partialOrderByTraversal to allow us to gracefully handle the case when the isolated and live trees might not be synced, and we can't traverse between nodes. Now that we return unordered, partialOrder callsites (text marker range constructors) can be updated to handle this case. * Source/WebCore/accessibility/AXTextMarker.cpp: (WebCore::AXTextMarkerRange::AXTextMarkerRange): (WebCore::AXTextMarker::wordRange const): (WebCore::AXTextMarker::partialOrderByTraversal const): Canonical link: https://commits.webkit.org/290862@main --- Source/WebCore/accessibility/AXTextMarker.cpp | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/Source/WebCore/accessibility/AXTextMarker.cpp b/Source/WebCore/accessibility/AXTextMarker.cpp index c48583e7fa9ff..939e605b9f030 100644 --- a/Source/WebCore/accessibility/AXTextMarker.cpp +++ b/Source/WebCore/accessibility/AXTextMarker.cpp @@ -276,14 +276,28 @@ AXTextMarkerRange::AXTextMarkerRange(const std::optional& range) AXTextMarkerRange::AXTextMarkerRange(const AXTextMarker& start, const AXTextMarker& end) { - bool reverse = is_gt(partialOrder(start, end)); + std::partial_ordering order = partialOrder(start, end); + if (order == std::partial_ordering::unordered) { + m_start = { }; + m_end = { }; + return; + } + + bool reverse = is_gt(order); m_start = reverse ? end : start; m_end = reverse ? start : end; } AXTextMarkerRange::AXTextMarkerRange(AXTextMarker&& start, AXTextMarker&& end) { - bool reverse = is_gt(partialOrder(start, end)); + std::partial_ordering order = partialOrder(start, end); + if (order == std::partial_ordering::unordered) { + m_start = { }; + m_end = { }; + return; + } + + bool reverse = is_gt(order); m_start = reverse ? WTFMove(end) : WTFMove(start); m_end = reverse ? WTFMove(start) : WTFMove(end); } @@ -1320,13 +1334,19 @@ AXTextMarkerRange AXTextMarker::wordRange(WordRangeType type) const endMarker = nextWordEnd(); startMarker = endMarker.previousWordStart(); // Don't return a right word if the word start is more than a position away from current text marker (e.g., there's a space between the word and current marker). - if (is_gt(partialOrder(startMarker, *this))) + std::partial_ordering order = partialOrder(startMarker, *this); + if (order == std::partial_ordering::unordered) + return { }; + if (is_gt(order)) return { *this, *this }; } else { startMarker = previousWordStart(); endMarker = startMarker.nextWordEnd(); // Don't return a left word if the word end is more than a position away from current text marker. - if (is_lt(partialOrder(endMarker, *this))) + std::partial_ordering order = partialOrder(endMarker, *this); + if (order == std::partial_ordering::unordered) + return { }; + if (is_lt(order)) return { *this, *this }; } @@ -1403,7 +1423,11 @@ std::partial_ordering AXTextMarker::partialOrderByTraversal(const AXTextMarker& if (current) return std::partial_ordering::greater; - RELEASE_ASSERT_NOT_REACHED(); + // It is possible to reach here if the live and isolated trees are not synced, and [next/previous]inPreOrder + // is unable to traverse between two nodes. This can happen when an element's parent or subtree is removed and + // those updates have not been fully applied. + // We don't release assert here, since the callers of partialOrder can now handle unordered ordering. + ASSERT_NOT_REACHED(); return std::partial_ordering::unordered; } From 13b9f7bb190fdf3927349d552cd510b271761eaf Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Fri, 21 Feb 2025 23:05:47 -0800 Subject: [PATCH 042/174] Remove `m_fullscreenElement` from `FullscreenManager` https://bugs.webkit.org/show_bug.cgi?id=288278 rdar://145344931 Reviewed by Alex Christensen. Use `fullscreenElement()` instead of storing the element as a member. * Source/WebCore/dom/Document.cpp: (WebCore::Document::removedLastRef): * Source/WebCore/dom/FullscreenManager.cpp: (WebCore::FullscreenManager::willEnterFullscreen): (WebCore::FullscreenManager::willExitFullscreen): (WebCore::FullscreenManager::didExitFullscreen): (WebCore::FullscreenManager::clear): Deleted. * Source/WebCore/dom/FullscreenManager.h: Canonical link: https://commits.webkit.org/290863@main --- Source/WebCore/dom/Document.cpp | 4 ---- Source/WebCore/dom/FullscreenManager.cpp | 18 ++++++------------ Source/WebCore/dom/FullscreenManager.h | 3 --- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp index 7f4225ce8672d..5fdffaab9471f 100644 --- a/Source/WebCore/dom/Document.cpp +++ b/Source/WebCore/dom/Document.cpp @@ -862,10 +862,6 @@ void Document::removedLastRef() m_focusNavigationStartingNode = nullptr; m_userActionElements.clear(); m_asyncNodeDeletionQueue.deleteNodesNow(); -#if ENABLE(FULLSCREEN_API) - if (CheckedPtr fullscreenManager = m_fullscreenManager.get()) - m_fullscreenManager->clear(); -#endif m_associatedFormControls.clear(); m_pendingRenderTreeUpdate = { }; diff --git a/Source/WebCore/dom/FullscreenManager.cpp b/Source/WebCore/dom/FullscreenManager.cpp index f6c0daff57cdf..4ccda8697f164 100644 --- a/Source/WebCore/dom/FullscreenManager.cpp +++ b/Source/WebCore/dom/FullscreenManager.cpp @@ -486,8 +486,6 @@ ExceptionOr FullscreenManager::willEnterFullscreen(Element& element, HTMLM #endif element.willBecomeFullscreenElement(); - m_fullscreenElement = &element; - Vector> ancestors { { element } }; for (RefPtr frame = element.document().frame(); frame; frame = frame->tree().parent()) { if (RefPtr ownerElement = frame->ownerElement()) @@ -541,9 +539,9 @@ bool FullscreenManager::didEnterFullscreen() bool FullscreenManager::willExitFullscreen() { - auto fullscreenElement = m_fullscreenElement; + RefPtr fullscreenElement = this->fullscreenElement(); if (!fullscreenElement) { - ERROR_LOG(LOGIDENTIFIER, "No fullscreenElement, bailing"); + ERROR_LOG(LOGIDENTIFIER, "No fullscreenElement; bailing"); return false; } @@ -566,15 +564,16 @@ void FullscreenManager::didExitFullscreen(CompletionHandlermainFrame(), ExitMode::Resize); - if (auto fullscreenElement = m_fullscreenElement) - fullscreenElement->didStopBeingFullscreenElement(); + if (exitedFullscreenElement) + exitedFullscreenElement->didStopBeingFullscreenElement(); m_areKeysEnabledInFullscreen = false; - m_fullscreenElement = nullptr; m_pendingExitFullscreen = false; completionHandler({ }); @@ -668,11 +667,6 @@ void FullscreenManager::setAnimatingFullscreen(bool flag) m_isAnimatingFullscreen = flag; } -void FullscreenManager::clear() -{ - m_fullscreenElement = nullptr; -} - void FullscreenManager::emptyEventQueue() { m_fullscreenChangeEventTargetQueue.clear(); diff --git a/Source/WebCore/dom/FullscreenManager.h b/Source/WebCore/dom/FullscreenManager.h index bc6ba8afcbe8e..9e76fa0eebe0f 100644 --- a/Source/WebCore/dom/FullscreenManager.h +++ b/Source/WebCore/dom/FullscreenManager.h @@ -88,7 +88,6 @@ class FullscreenManager final : public CanMakeWeakPtr, public WEBCORE_EXPORT bool isAnimatingFullscreen() const; WEBCORE_EXPORT void setAnimatingFullscreen(bool); - void clear(); void emptyEventQueue(); protected: @@ -115,8 +114,6 @@ class FullscreenManager final : public CanMakeWeakPtr, public WeakRef m_document; - RefPtr m_fullscreenElement; - Deque> m_fullscreenChangeEventTargetQueue; Deque> m_fullscreenErrorEventTargetQueue; From 7721708e4c99720795d94212a93e7ae46c19f427 Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Fri, 21 Feb 2025 23:31:49 -0800 Subject: [PATCH 043/174] Align document.importNode() with the latest proposal https://bugs.webkit.org/show_bug.cgi?id=288193 rdar://145287338 Reviewed by Ryosuke Niwa. When importNode() is used in the "modern" fashion we want it to clone the children by default. Therefore we rename the options member from deep to selfOnly. New tests are proposed upstream here: https://github.com/web-platform-tests/wpt/pull/50860 Variable rename from "internals" to "elementInternals" in existing tests was a change made upstream to account for linting rules. * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Construct.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-define.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-initialize.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-upgrade.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Document-createElement.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Document-importNode.tentative-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Document-importNode.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements-exceptions.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-innerHTML.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/ShadowRoot-init-customElements.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/ShadowRoot-innerHTML.tentative.html: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/w3c-import.log: Added. * Source/WebCore/dom/Document.cpp: (WebCore::Document::importNode): * Source/WebCore/dom/Document.h: * Source/WebCore/dom/Document.idl: * Source/WebCore/dom/ImportNodeOptions.h: * Source/WebCore/dom/ImportNodeOptions.idl: Canonical link: https://commits.webkit.org/290864@main --- .../Construct.tentative.html | 4 +- ...ustomElementRegistry-define.tentative.html | 4 +- ...mElementRegistry-initialize.tentative.html | 4 +- ...stomElementRegistry-upgrade.tentative.html | 52 ++++++------- .../Document-createElement.tentative.html | 4 +- ...Document-importNode.tentative-expected.txt | 9 +++ .../Document-importNode.tentative.html | 73 +++++++++++++++---- ...t-customElements-exceptions.tentative.html | 4 +- .../Element-customElements.tentative.html | 4 +- .../Element-innerHTML.tentative.html | 4 +- ...dowRoot-init-customElements.tentative.html | 4 +- .../ShadowRoot-innerHTML.tentative.html | 4 +- .../revamped-scoped-registry/w3c-import.log | 27 +++++++ Source/WebCore/dom/Document.cpp | 21 +++--- Source/WebCore/dom/Document.h | 2 +- Source/WebCore/dom/Document.idl | 2 +- Source/WebCore/dom/ImportNodeOptions.h | 2 +- Source/WebCore/dom/ImportNodeOptions.idl | 4 +- 18 files changed, 154 insertions(+), 74 deletions(-) create mode 100644 LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/w3c-import.log diff --git a/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Construct.tentative.html b/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Construct.tentative.html index b73d2c997455b..8233e6832eb92 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Construct.tentative.html +++ b/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Construct.tentative.html @@ -3,8 +3,8 @@ - - + + - + + - + +
diff --git a/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-upgrade.tentative.html b/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-upgrade.tentative.html index b3e5b3146844c..575b23e0ee447 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-upgrade.tentative.html +++ b/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-upgrade.tentative.html @@ -3,8 +3,8 @@ - - + + @@ -23,11 +23,11 @@ - + + - + +
@@ -25,18 +25,21 @@ +
+ +
diff --git a/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements-exceptions.tentative.html b/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements-exceptions.tentative.html index a4f29527cbc4f..4442c04c53de4 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements-exceptions.tentative.html +++ b/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements-exceptions.tentative.html @@ -3,8 +3,8 @@ - - + +
diff --git a/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements.tentative.html b/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements.tentative.html index b1709c63e90ad..e27074d5eb650 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements.tentative.html +++ b/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements.tentative.html @@ -3,8 +3,8 @@ - - + +
diff --git a/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-innerHTML.tentative.html b/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-innerHTML.tentative.html index d5ec0f8c83808..dcbbbdcd673e7 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-innerHTML.tentative.html +++ b/LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-innerHTML.tentative.html @@ -3,8 +3,8 @@ - - + + - + + - + + + + +
+
+
+
+
+
+ diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp index ff6b983dbac21..2b28063c7a5f9 100644 --- a/Source/WebCore/dom/Document.cpp +++ b/Source/WebCore/dom/Document.cpp @@ -2756,7 +2756,7 @@ void Document::resolveStyle(ResolveStyleType type) updateRenderTree(WTFMove(styleUpdate)); if (frameView->layoutContext().needsLayout()) - frameView->layoutContext().layout(); + frameView->layoutContext().interleavedLayout(); } styleUpdate = resolver.resolve(); diff --git a/Source/WebCore/page/LocalFrameViewLayoutContext.cpp b/Source/WebCore/page/LocalFrameViewLayoutContext.cpp index 9ef5c52442c47..e07586015e47b 100644 --- a/Source/WebCore/page/LocalFrameViewLayoutContext.cpp +++ b/Source/WebCore/page/LocalFrameViewLayoutContext.cpp @@ -172,6 +172,18 @@ void LocalFrameViewLayoutContext::layout(bool canDeferUpdateLayerPositions) } } +void LocalFrameViewLayoutContext::interleavedLayout() +{ + LOG_WITH_STREAM(Layout, stream << "LocalFrameView " << &view() << " LocalFrameViewLayoutContext::interleavedLayout() with size " << view().layoutSize()); + + Ref protectedView(view()); + + performLayout(false); + + Style::Scope::LayoutDependencyUpdateContext layoutDependencyUpdateContext; + document()->styleScope().invalidateForLayoutDependencies(layoutDependencyUpdateContext); +} + void LocalFrameViewLayoutContext::performLayout(bool canDeferUpdateLayerPositions) { Ref frame = this->frame(); diff --git a/Source/WebCore/page/LocalFrameViewLayoutContext.h b/Source/WebCore/page/LocalFrameViewLayoutContext.h index a37555a5b8bcf..ceed9a01271ec 100644 --- a/Source/WebCore/page/LocalFrameViewLayoutContext.h +++ b/Source/WebCore/page/LocalFrameViewLayoutContext.h @@ -70,6 +70,8 @@ class LocalFrameViewLayoutContext final : public CanMakeCheckedPtr layoutOptions = { }) const; + void interleavedLayout(); + // We rely on the side-effects of layout, like compositing updates, to update state in various subsystems // whose dependencies are poorly defined. This call triggers such updates. void setNeedsLayoutAfterViewConfigurationChange(); diff --git a/Source/WebCore/rendering/RenderBox.cpp b/Source/WebCore/rendering/RenderBox.cpp index 623e9189a0e8d..fd95e7772dab5 100644 --- a/Source/WebCore/rendering/RenderBox.cpp +++ b/Source/WebCore/rendering/RenderBox.cpp @@ -166,6 +166,8 @@ void RenderBox::willBeDestroyed() view().unregisterContainerQueryBox(*this); if (!style().anchorNames().isEmpty()) view().unregisterAnchor(*this); + if (!style().positionTryFallbacks().isEmpty()) + view().unregisterPositionTryBox(*this); } RenderBoxModelObject::willBeDestroyed(); @@ -300,6 +302,11 @@ void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle& newStyl else if (oldStyle && !oldStyle->anchorNames().isEmpty()) view().unregisterAnchor(*this); + if (!style().positionTryFallbacks().isEmpty() && style().hasOutOfFlowPosition()) + view().registerPositionTryBox(*this); + else if (oldStyle && !oldStyle->positionTryFallbacks().isEmpty() && oldStyle->hasOutOfFlowPosition()) + view().unregisterPositionTryBox(*this); + RenderBoxModelObject::styleWillChange(diff, newStyle); } diff --git a/Source/WebCore/rendering/RenderView.cpp b/Source/WebCore/rendering/RenderView.cpp index fc077af82e394..12f8d043564d3 100644 --- a/Source/WebCore/rendering/RenderView.cpp +++ b/Source/WebCore/rendering/RenderView.cpp @@ -1100,6 +1100,16 @@ void RenderView::unregisterAnchor(const RenderBoxModelObject& renderer) m_anchors.remove(renderer); } +void RenderView::registerPositionTryBox(const RenderBox& box) +{ + m_positionTryBoxes.add(box); +} + +void RenderView::unregisterPositionTryBox(const RenderBox& box) +{ + m_positionTryBoxes.remove(box); +} + void RenderView::addCounterNeedingUpdate(RenderCounter& renderer) { m_countersNeedingUpdate.add(renderer); diff --git a/Source/WebCore/rendering/RenderView.h b/Source/WebCore/rendering/RenderView.h index c69eab050e403..c3f6edb23f0cf 100644 --- a/Source/WebCore/rendering/RenderView.h +++ b/Source/WebCore/rendering/RenderView.h @@ -212,6 +212,10 @@ class RenderView final : public RenderBlockFlow { void unregisterAnchor(const RenderBoxModelObject&); const SingleThreadWeakHashSet& anchors() const { return m_anchors; } + void registerPositionTryBox(const RenderBox&); + void unregisterPositionTryBox(const RenderBox&); + const SingleThreadWeakHashSet& positionTryBoxes() const { return m_positionTryBoxes; } + SingleThreadWeakPtr viewTransitionRoot() const; void setViewTransitionRoot(RenderElement& renderer); @@ -287,6 +291,7 @@ class RenderView final : public RenderBlockFlow { SingleThreadWeakHashSet m_boxesWithScrollSnapPositions; SingleThreadWeakHashSet m_containerQueryBoxes; SingleThreadWeakHashSet m_anchors; + SingleThreadWeakHashSet m_positionTryBoxes; SingleThreadWeakPtr m_viewTransitionRoot; HashMap> m_viewTransitionGroups; diff --git a/Source/WebCore/style/AnchorPositionEvaluator.cpp b/Source/WebCore/style/AnchorPositionEvaluator.cpp index 8426d6a78efb5..eee1ddf57eb6e 100644 --- a/Source/WebCore/style/AnchorPositionEvaluator.cpp +++ b/Source/WebCore/style/AnchorPositionEvaluator.cpp @@ -32,6 +32,7 @@ #include "DocumentInlines.h" #include "Element.h" #include "Node.h" +#include "RenderBoxInlines.h" #include "RenderBlock.h" #include "RenderBoxModelObjectInlines.h" #include "RenderFragmentedFlow.h" @@ -276,15 +277,41 @@ LayoutRect AnchorPositionEvaluator::computeAnchorRectRelativeToContainingBlock(C // align the edge of the positioned elements' inset-modified containing block corresponding to the // property the function appears in with the specified border edge of the target anchor element..." // See: https://drafts.csswg.org/css-anchor-position-1/#anchor-pos -static LayoutUnit computeInsetValue(CSSPropertyID insetPropertyID, CheckedRef anchorBox, CheckedRef anchorPositionedRenderer, AnchorPositionEvaluator::Side anchorSide) +static LayoutUnit computeInsetValue(CSSPropertyID insetPropertyID, CheckedRef anchorBox, CheckedRef anchorPositionedRenderer, AnchorPositionEvaluator::Side anchorSide, const std::optional& positionTryFallback) { CheckedPtr containingBlock = anchorPositionedRenderer->containingBlock(); ASSERT(containingBlock); - auto insetPropertySide = mapInsetPropertyToPhysicalSide(insetPropertyID, anchorPositionedRenderer->writingMode()); + auto writingMode = anchorPositionedRenderer->writingMode(); + auto insetPropertySide = mapInsetPropertyToPhysicalSide(insetPropertyID, writingMode); auto anchorSideID = std::holds_alternative(anchorSide) ? std::get(anchorSide) : CSSValueInvalid; auto anchorRect = AnchorPositionEvaluator::computeAnchorRectRelativeToContainingBlock(anchorBox, *containingBlock); + bool shouldFlipForPositionTryFallback = [&] { + if (!positionTryFallback) + return false; + + auto shouldFlipForAxis = [&](LogicalBoxAxis logicalAxis) { + auto axis = mapAxisLogicalToPhysical(writingMode, logicalAxis); + if (axis == BoxAxis::Horizontal) { + if (insetPropertySide == BoxSide::Left || insetPropertySide == BoxSide::Right) + return true; + } else if (insetPropertySide == BoxSide::Top || insetPropertySide == BoxSide::Bottom) + return true; + return false; + }; + + if (positionTryFallback->tactics.contains(PositionTryFallback::Tactic::FlipInline)) { + if (shouldFlipForAxis(LogicalBoxAxis::Inline)) + return true; + } + if (positionTryFallback->tactics.contains(PositionTryFallback::Tactic::FlipBlock)) { + if (shouldFlipForAxis(LogicalBoxAxis::Block)) + return true; + } + return false; + }(); + // Explicitly deal with the center/percentage value here. // "Refers to a position a corresponding percentage between the start and end sides, with // 0% being equivalent to start and 100% being equivalent to end. center is equivalent to 50%." @@ -297,8 +324,11 @@ static LayoutUnit computeInsetValue(CSSPropertyID insetPropertyID, CheckedRefwritingMode()); + auto insetPropertyAxis = mapInsetPropertyToPhysicalAxis(insetPropertyID, writingMode); if (insetPropertyAxis == BoxAxis::Vertical) { insetValue = anchorRect.location().y() + anchorRect.height() * percentage; if (insetPropertySide == BoxSide::Bottom) @@ -350,6 +380,11 @@ static LayoutUnit computeInsetValue(CSSPropertyID insetPropertyID, CheckedRef AnchorPositionEvaluator::evaluate(const BuilderState& buil // Proceed with computing the inset value for the specified inset property. CheckedRef anchorBox = downcast(*anchorRenderer); - return computeInsetValue(propertyID, anchorBox, *anchorPositionedRenderer, side); + return computeInsetValue(propertyID, anchorBox, *anchorPositionedRenderer, side, builderState.positionTryFallback()); } // Returns the default anchor size dimension to use when it is not specified in @@ -858,6 +893,66 @@ bool AnchorPositionEvaluator::isLayoutTimeAnchorPositioned(const RenderStyle& st return style.justifySelf().position() == ItemPosition::AnchorCenter || style.alignSelf().position() == ItemPosition::AnchorCenter; } +static CSSPropertyID flipHorizontal(CSSPropertyID propertyID) +{ + switch (propertyID) { + case CSSPropertyLeft: + return CSSPropertyRight; + case CSSPropertyRight: + return CSSPropertyLeft; + case CSSPropertyMarginLeft: + return CSSPropertyMarginRight; + case CSSPropertyMarginRight: + return CSSPropertyMarginLeft; + default: + return propertyID; + } +} + +static CSSPropertyID flipVertical(CSSPropertyID propertyID) +{ + switch (propertyID) { + case CSSPropertyTop: + return CSSPropertyBottom; + case CSSPropertyBottom: + return CSSPropertyTop; + case CSSPropertyMarginTop: + return CSSPropertyMarginBottom; + case CSSPropertyMarginBottom: + return CSSPropertyMarginTop; + default: + return propertyID; + } +} + +CSSPropertyID AnchorPositionEvaluator::resolvePositionTryFallbackProperty(CSSPropertyID propertyID, WritingMode writingMode, const PositionTryFallback& fallback) +{ + ASSERT(!CSSProperty::isDirectionAwareProperty(propertyID)); + + for (auto tactic : fallback.tactics) { + switch (tactic) { + case PositionTryFallback::Tactic::FlipInline: + propertyID = writingMode.isHorizontal() ? flipHorizontal(propertyID) : flipVertical(propertyID); + break; + case PositionTryFallback::Tactic::FlipBlock: + propertyID = writingMode.isHorizontal() ? flipVertical(propertyID) : flipHorizontal(propertyID); + break; + case PositionTryFallback::Tactic::FlipStart: + break; + } + } + return propertyID; +} + +bool AnchorPositionEvaluator::overflowsContainingBlock(const RenderBox& anchoredBox) +{ + auto containingBlockRect = anchoredBox.containingBlock()->contentBoxRect(); + + auto marginRect = anchoredBox.marginBoxRect(); + marginRect.moveBy(anchoredBox.location()); + + return !containingBlockRect.contains(marginRect); +} WTF::TextStream& operator<<(WTF::TextStream& ts, PositionTryOrder order) { diff --git a/Source/WebCore/style/AnchorPositionEvaluator.h b/Source/WebCore/style/AnchorPositionEvaluator.h index 96fe532dc4b38..d1ba22221e307 100644 --- a/Source/WebCore/style/AnchorPositionEvaluator.h +++ b/Source/WebCore/style/AnchorPositionEvaluator.h @@ -28,6 +28,7 @@ #include "EventTarget.h" #include "LayoutUnit.h" #include "ScopedName.h" +#include "WritingMode.h" #include #include #include @@ -40,8 +41,10 @@ class Document; class Element; class LayoutRect; class RenderBlock; +class RenderBox; class RenderBoxModelObject; class RenderStyle; +struct PositionTryFallback; enum CSSPropertyID : uint16_t; @@ -116,6 +119,9 @@ class AnchorPositionEvaluator { static AnchorToAnchorPositionedMap makeAnchorPositionedForAnchorMap(Document&); static bool isLayoutTimeAnchorPositioned(const RenderStyle&); + static CSSPropertyID resolvePositionTryFallbackProperty(CSSPropertyID, WritingMode, const PositionTryFallback&); + + static bool overflowsContainingBlock(const RenderBox& anchoredBox); private: static AnchorElements findAnchorsForAnchorPositionedElement(const Element&, const UncheckedKeyHashSet& anchorNames, const AnchorsForAnchorName&); diff --git a/Source/WebCore/style/StyleBuilder.cpp b/Source/WebCore/style/StyleBuilder.cpp index 5c0c24607252c..cc5d89294655c 100644 --- a/Source/WebCore/style/StyleBuilder.cpp +++ b/Source/WebCore/style/StyleBuilder.cpp @@ -295,6 +295,9 @@ void Builder::applyProperty(CSSPropertyID id, CSSValue& value, SelectorChecker:: return applyProperty(newId, valueToApply.get(), linkMatchMask, cascadeLevel); } + if (m_state.positionTryFallback()) + id = AnchorPositionEvaluator::resolvePositionTryFallbackProperty(id, style.writingMode(), *m_state.positionTryFallback()); + auto valueID = WebCore::valueID(valueToApply.get()); const CSSCustomPropertyValue* customPropertyValue = nullptr; diff --git a/Source/WebCore/style/StyleBuilderState.h b/Source/WebCore/style/StyleBuilderState.h index e117c2e4175a3..f6c4834f597b4 100644 --- a/Source/WebCore/style/StyleBuilderState.h +++ b/Source/WebCore/style/StyleBuilderState.h @@ -28,6 +28,7 @@ #include "CSSToLengthConversionData.h" #include "CSSToStyleMap.h" #include "CascadeLevel.h" +#include "PositionTryFallback.h" #include "PropertyCascade.h" #include "RuleSet.h" #include "SelectorChecker.h" @@ -66,6 +67,7 @@ struct BuilderContext { const RenderStyle& parentStyle; const RenderStyle* rootElementStyle = nullptr; RefPtr element = nullptr; + std::optional positionTryFallback { }; }; class BuilderState { @@ -130,6 +132,8 @@ class BuilderState { Ref randomKeyMap(bool perElement) const; + const std::optional& positionTryFallback() const { return m_context.positionTryFallback; } + private: // See the comment in maybeUpdateFontForLetterSpacing() about why this needs to be a friend. friend void maybeUpdateFontForLetterSpacing(BuilderState&, CSSValue&); diff --git a/Source/WebCore/style/StyleScope.cpp b/Source/WebCore/style/StyleScope.cpp index 1f8dd1805a69f..fa69791be46f2 100644 --- a/Source/WebCore/style/StyleScope.cpp +++ b/Source/WebCore/style/StyleScope.cpp @@ -926,7 +926,9 @@ bool Scope::isForUserAgentShadowTree() const bool Scope::invalidateForLayoutDependencies(LayoutDependencyUpdateContext& context) { - return invalidateForContainerDependencies(context) || invalidateForAnchorDependencies(context); + return invalidateForContainerDependencies(context) + || invalidateForAnchorDependencies(context) + || invalidateForPositionTryFallbacks(context); } bool Scope::invalidateForContainerDependencies(LayoutDependencyUpdateContext& context) @@ -1028,6 +1030,34 @@ bool Scope::invalidateForAnchorDependencies(LayoutDependencyUpdateContext& conte return !anchoredElementsToInvalidate.isEmpty(); } +bool Scope::invalidateForPositionTryFallbacks(LayoutDependencyUpdateContext& context) +{ + ASSERT(!m_shadowRoot); + + if (!m_document->renderView()) + return false; + + bool invalidated = false; + + for (auto& box : m_document->renderView()->positionTryBoxes()) { + if (!AnchorPositionEvaluator::overflowsContainingBlock(box)) + continue; + + CheckedPtr element = box.element(); + if (auto* pseudoElement = dynamicDowncast(element.get())) + element = pseudoElement->hostElement(); + + if (element) { + if (!context.invalidatedAnchorPositioned.add(*element).isNewEntry) + continue; + element->invalidateForAnchorRectChange(); + invalidated = true; + } + } + + return invalidated; +} + const MatchResult* Scope::cachedMatchResult(const Element& element) { auto it = m_cachedMatchResults.find(element); diff --git a/Source/WebCore/style/StyleScope.h b/Source/WebCore/style/StyleScope.h index a2d48e22cc7cf..db1dd2706cb18 100644 --- a/Source/WebCore/style/StyleScope.h +++ b/Source/WebCore/style/StyleScope.h @@ -216,6 +216,7 @@ class Scope final : public CanMakeWeakPtr, public CanMakeCheckedPtr m_document; ShadowRoot* m_shadowRoot { nullptr }; diff --git a/Source/WebCore/style/StyleTreeResolver.cpp b/Source/WebCore/style/StyleTreeResolver.cpp index 4304eda510474..2b4198ac40c85 100644 --- a/Source/WebCore/style/StyleTreeResolver.cpp +++ b/Source/WebCore/style/StyleTreeResolver.cpp @@ -45,6 +45,7 @@ #include "NodeRenderStyle.h" #include "Page.h" #include "PlatformStrategies.h" +#include "PositionTryFallback.h" #include "Quirks.h" #include "RenderElement.h" #include "RenderStyleSetters.h" @@ -150,6 +151,9 @@ ResolvedStyle TreeResolver::styleForStyleable(const Styleable& styleable, Resolu auto& element = styleable.element; + if (auto optionStyle = tryChoosePositionOption(styleable, existingStyle)) + return WTFMove(*optionStyle); + if (element.hasCustomStyleResolveCallbacks()) { RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr; if (auto customStyle = element.resolveCustomStyle(resolutionContext, shadowHostStyle)) { @@ -279,6 +283,9 @@ auto TreeResolver::resolveElement(Element& element, const RenderStyle* existingS Styleable styleable { element, { } }; auto resolvedStyle = styleForStyleable(styleable, resolutionType, resolutionContext, existingStyle); + + generatePositionOptionsIfNeeded(resolvedStyle, styleable, resolutionContext); + auto update = createAnimatedElementUpdate(WTFMove(resolvedStyle), styleable, parent().change, resolutionContext, parent().isInDisplayNoneTree); if (!affectsRenderedSubtree(element, *update.style)) { @@ -807,7 +814,7 @@ std::unique_ptr TreeResolver::resolveStartingStyle(const ResolvedSt // We now resolve the starting style by applying all rules (including @starting-style ones) again. // We could compute it along with the primary style and include it in MatchedPropertiesCache but it is not // clear this would be benefitial as it is typically only used once. - return resolveAgainWithParentStyle(resolvedStyle, styleable, parentStyle, PropertyCascade::startingStyleProperties(), resolutionContext); + return resolveAgainInDifferentContext(resolvedStyle, styleable, parentStyle, PropertyCascade::startingStyleProperties(), { }, resolutionContext); } std::unique_ptr TreeResolver::resolveAfterChangeStyleForNonAnimated(const ResolvedStyle& resolvedStyle, const Styleable& styleable, const ResolutionContext& resolutionContext) const @@ -828,10 +835,10 @@ std::unique_ptr TreeResolver::resolveAfterChangeStyleForNonAnimated // "Likewise, define the after-change style as.. and inheriting from the after-change style of the parent." auto& parentStyle = parentAfterChangeStyle(styleable, resolutionContext); - return resolveAgainWithParentStyle(resolvedStyle, styleable, parentStyle, PropertyCascade::normalProperties(), resolutionContext); + return resolveAgainInDifferentContext(resolvedStyle, styleable, parentStyle, PropertyCascade::normalProperties(), { }, resolutionContext); } -std::unique_ptr TreeResolver::resolveAgainWithParentStyle(const ResolvedStyle& resolvedStyle, const Styleable& styleable, const RenderStyle& parentStyle, OptionSet properties, const ResolutionContext& resolutionContext) const +std::unique_ptr TreeResolver::resolveAgainInDifferentContext(const ResolvedStyle& resolvedStyle, const Styleable& styleable, const RenderStyle& parentStyle, OptionSet properties, const std::optional& positionTryFallback, const ResolutionContext& resolutionContext) const { ASSERT(resolvedStyle.matchResult); @@ -845,7 +852,8 @@ std::unique_ptr TreeResolver::resolveAgainWithParentStyle(const Res m_document.get(), parentStyle, resolutionContext.documentElementStyle, - &styleable.element + &styleable.element, + positionTryFallback }; auto styleBuilder = Builder { @@ -1175,7 +1183,7 @@ void TreeResolver::resolveComposedTree() it.traverseNextSkippingChildren(); continue; } - + resetDescendantStyleRelations(element, descendantsToResolve); auto isInDisplayNoneTree = parent.isInDisplayNoneTree == IsInDisplayNoneTree::Yes || !style || style->display() == DisplayType::None; @@ -1279,6 +1287,13 @@ std::unique_ptr TreeResolver::resolve() } } + for (auto& elementAndOptions : m_positionOptions) { + if (!elementAndOptions.value.chosen) { + elementAndOptions.key->invalidateForResumingAnchorPositionedElementResolution(); + m_hasUnresolvedAnchorPositionedElements = true; + } + } + if (m_update->roots().isEmpty()) return { }; @@ -1305,6 +1320,85 @@ auto TreeResolver::updateAnchorPositioningState(Element& element, const RenderSt return AnchorPositionedElementAction::SkipDescendants; } +void TreeResolver::generatePositionOptionsIfNeeded(const ResolvedStyle& resolvedStyle, const Styleable& styleable, const ResolutionContext& resolutionContext) +{ + // https://drafts.csswg.org/css-anchor-position-1/#fallback-apply + + if (!resolvedStyle.style || resolvedStyle.style->positionTryFallbacks().isEmpty()) + return; + + auto* anchorPositionedState = m_document->styleScope().anchorPositionedStates().get(styleable.element); + if (anchorPositionedState && anchorPositionedState->stage < AnchorPositionResolutionStage::Resolved) + return; + + m_positionOptions.ensure(styleable.element, [&] { + auto options = PositionOptions { .originalStyle = RenderStyle::clonePtr(*resolvedStyle.style) }; + options.optionStyles.reserveInitialCapacity(resolvedStyle.style->positionTryFallbacks().size()); + for (auto& fallback : resolvedStyle.style->positionTryFallbacks()) { + auto optionStyle = generatePositionOption(fallback, resolvedStyle, styleable, resolutionContext); + if (!optionStyle) + continue; + options.optionStyles.append(WTFMove(optionStyle)); + } + return options; + }); +} + +std::unique_ptr TreeResolver::generatePositionOption(const PositionTryFallback& fallback, const ResolvedStyle& resolvedStyle, const Styleable& styleable, const ResolutionContext& resolutionContext) +{ + // https://drafts.csswg.org/css-anchor-position-1/#fallback-apply + + if (!resolvedStyle.matchResult) + return { }; + + return resolveAgainInDifferentContext(resolvedStyle, styleable, *resolutionContext.parentStyle, PropertyCascade::normalProperties(), fallback, resolutionContext); +} + +std::optional TreeResolver::tryChoosePositionOption(const Styleable& styleable, const RenderStyle* existingStyle) +{ + // https://drafts.csswg.org/css-anchor-position-1/#fallback-apply + + if (!existingStyle) + return { }; + + auto optionIt = m_positionOptions.find(styleable.element); + if (optionIt == m_positionOptions.end()) + return { }; + + auto& options = optionIt->value; + + auto renderer = dynamicDowncast(styleable.element.renderer()); + if (!renderer) { + options.chosen = true; + return ResolvedStyle { RenderStyle::clonePtr(*options.originalStyle) }; + } + + if (!AnchorPositionEvaluator::overflowsContainingBlock(*renderer)) { + // We don't overflow anymore so this is a good style. + options.chosen = true; + return ResolvedStyle { RenderStyle::clonePtr(*existingStyle) }; + } + + if (options.chosen) { + // We have already chosen. + return ResolvedStyle { RenderStyle::clonePtr(*existingStyle) }; + } + + if (options.index >= options.optionStyles.size()) { + // None of the options worked, return back to the original. + options.chosen = true; + return ResolvedStyle { RenderStyle::clonePtr(*options.originalStyle) }; + } + + auto& optionStyle = options.optionStyles[options.index]; + + // Next option to try if this doesn't work. + ++options.index; + + return ResolvedStyle { RenderStyle::clonePtr(*optionStyle) }; +} + + static Vector>& postResolutionCallbackQueue() { static NeverDestroyed>> vector; diff --git a/Source/WebCore/style/StyleTreeResolver.h b/Source/WebCore/style/StyleTreeResolver.h index ce0d322e8fbe4..57eef8b3e7a69 100644 --- a/Source/WebCore/style/StyleTreeResolver.h +++ b/Source/WebCore/style/StyleTreeResolver.h @@ -43,6 +43,7 @@ class Element; class Node; class RenderStyle; class ShadowRoot; +struct PositionTryFallback; namespace Style { @@ -84,7 +85,7 @@ class TreeResolver { ElementUpdate createAnimatedElementUpdate(ResolvedStyle&&, const Styleable&, Change, const ResolutionContext&, IsInDisplayNoneTree = IsInDisplayNoneTree::No); std::unique_ptr resolveStartingStyle(const ResolvedStyle&, const Styleable&, const ResolutionContext&) const; std::unique_ptr resolveAfterChangeStyleForNonAnimated(const ResolvedStyle&, const Styleable&, const ResolutionContext&) const; - std::unique_ptr resolveAgainWithParentStyle(const ResolvedStyle&, const Styleable&, const RenderStyle& parentStyle, OptionSet, const ResolutionContext&) const; + std::unique_ptr resolveAgainInDifferentContext(const ResolvedStyle&, const Styleable&, const RenderStyle& parentStyle, OptionSet, const std::optional&, const ResolutionContext&) const; const RenderStyle& parentAfterChangeStyle(const Styleable&, const ResolutionContext&) const; UncheckedKeyHashSet applyCascadeAfterAnimation(RenderStyle&, const UncheckedKeyHashSet&, bool isTransition, const MatchResult&, const Element&, const ResolutionContext&); @@ -148,6 +149,10 @@ class TreeResolver { AnchorPositionedElementAction updateAnchorPositioningState(Element&, const RenderStyle*); + void generatePositionOptionsIfNeeded(const ResolvedStyle&, const Styleable&, const ResolutionContext&); + std::unique_ptr generatePositionOption(const PositionTryFallback&, const ResolvedStyle&, const Styleable&, const ResolutionContext&); + std::optional tryChoosePositionOption(const Styleable&, const RenderStyle* existingStyle); + struct QueryContainerState { Change change { Change::None }; DescendantsToResolve descendantsToResolve { DescendantsToResolve::None }; @@ -164,6 +169,14 @@ class TreeResolver { bool m_hasUnresolvedQueryContainers { false }; bool m_hasUnresolvedAnchorPositionedElements { false }; + struct PositionOptions { + std::unique_ptr originalStyle; + Vector> optionStyles { }; + size_t index { 0 }; + bool chosen { false }; + }; + HashMap, PositionOptions> m_positionOptions; + std::unique_ptr m_update; }; From 92ab86c96733339731d0c2abfeec831624851035 Mon Sep 17 00:00:00 2001 From: Charlie Wolfe Date: Sat, 22 Feb 2025 13:22:36 -0800 Subject: [PATCH 071/174] REGRESSION(288357@main): Crash in `WebHistoryItemClient::historyItemChanged` https://bugs.webkit.org/show_bug.cgi?id=288279 rdar://142947060 Reviewed by Alex Christensen. WebHistoryItemClient can outlive its WebPage, so it should hold a WeakPtr to WebPage instead of a WeakRef. * Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.cpp: (WebKit::WebHistoryItemClient::historyItemChanged): (WebKit::WebHistoryItemClient::clearChildren const): * Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.h: Canonical link: https://commits.webkit.org/290892@main --- Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.cpp | 6 ++++-- Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.cpp b/Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.cpp index 985044c1265b4..e8efeb1e5bc80 100644 --- a/Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.cpp +++ b/Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.cpp @@ -52,14 +52,16 @@ void WebHistoryItemClient::historyItemChanged(const WebCore::HistoryItem& item) { if (m_shouldIgnoreChanges) return; - m_page->send(Messages::WebPageProxy::BackForwardUpdateItem(toFrameState(item))); + if (RefPtr page = m_page.get()) + page->send(Messages::WebPageProxy::BackForwardUpdateItem(toFrameState(item))); } void WebHistoryItemClient::clearChildren(const WebCore::HistoryItem& item) const { if (m_shouldIgnoreChanges) return; - m_page->send(Messages::WebPageProxy::BackForwardClearChildren(item.itemID(), item.frameItemID())); + if (RefPtr page = m_page.get()) + page->send(Messages::WebPageProxy::BackForwardClearChildren(item.itemID(), item.frameItemID())); } } diff --git a/Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.h b/Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.h index 069acbdd0069c..f3fc5c28e74bb 100644 --- a/Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.h +++ b/Source/WebKit/WebProcess/WebPage/WebHistoryItemClient.h @@ -43,7 +43,7 @@ class WebHistoryItemClient final : public WebCore::HistoryItemClient { void historyItemChanged(const WebCore::HistoryItem&) final; void clearChildren(const WebCore::HistoryItem&) const final; - const WeakRef m_page; + const WeakPtr m_page; bool m_shouldIgnoreChanges { false }; }; From 0aca7bdaf628ee0b9ddf5d444df66f14e3dbd515 Mon Sep 17 00:00:00 2001 From: Tyler Wilcock Date: Sat, 22 Feb 2025 15:43:29 -0800 Subject: [PATCH 072/174] AX: After dynamic page changes, WebKit can report the wrong AXEmptyGroup subrole state to assistive technologies https://bugs.webkit.org/show_bug.cgi?id=288215 rdar://145306055 Reviewed by Chris Fleizach. We cache AXProperty::SubrolePlatformString, but never update it after dynamic page changes, meaning we can return a stale value when considering AXEmptyGroup, which is dependent on whether an object has unignored children. This was never a problem prior to ENABLE(INCLUDE_IGNORED_IN_CORE_TREE), as anytime an object's unignored chlidren changed, we created a whole new node change for it. But with this feature, we no longer do that, since each object's unignored children is dependent on arbitrary layers of descendants underneath it. Fix this by computing isEmptyGroup lazily off the main-thread, ensuring we never use stale information. * LayoutTests/accessibility/mac/dynamic-empty-group-expected.txt: Added. * LayoutTests/accessibility/mac/dynamic-empty-group.html: Added. * Source/WebCore/accessibility/AXCoreObject.cpp: (WebCore::isValidChildForTable): (WebCore::AXCoreObject::unignoredChildren): * Source/WebCore/accessibility/AXCoreObject.h: (WebCore::AXCoreObject::onlyAddsUnignoredChildren const): * Source/WebCore/accessibility/AXSearchManager.cpp: (WebCore::appendChildrenToArray): * Source/WebCore/accessibility/cocoa/AXCoreObjectCocoa.mm: (WebCore::renderWidgetChildren): (WebCore::AXCoreObject::isEmptyGroup): * Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm: (WebCore::AccessibilityObject::subrolePlatformString const): (WebCore::isEmptyGroup): Deleted. (WebCore::renderWidgetChildren): Deleted. * Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm: (subroleString): Canonical link: https://commits.webkit.org/290893@main --- .../mac/dynamic-empty-group-expected.txt | 9 +++++ .../mac/dynamic-empty-group.html | 34 ++++++++++++++++++ Source/WebCore/accessibility/AXCoreObject.cpp | 31 ++++++++++++---- Source/WebCore/accessibility/AXCoreObject.h | 11 +++++- .../accessibility/cocoa/AXCoreObjectCocoa.mm | 33 +++++++++++++++++ .../mac/AccessibilityObjectMac.mm | 35 ------------------- .../mac/WebAccessibilityObjectWrapperMac.mm | 3 ++ 7 files changed, 114 insertions(+), 42 deletions(-) create mode 100644 LayoutTests/accessibility/mac/dynamic-empty-group-expected.txt create mode 100644 LayoutTests/accessibility/mac/dynamic-empty-group.html diff --git a/LayoutTests/accessibility/mac/dynamic-empty-group-expected.txt b/LayoutTests/accessibility/mac/dynamic-empty-group-expected.txt new file mode 100644 index 0000000000000..3b697ecb345fd --- /dev/null +++ b/LayoutTests/accessibility/mac/dynamic-empty-group-expected.txt @@ -0,0 +1,9 @@ +This test ensures we report or don't report the AXEmptyGroup subrole after dynamic page changes. + +PASS: accessibilityController.accessibleElementById('target').subrole === 'AXSubrole: AXEmptyGroup' +PASS: accessibilityController.accessibleElementById('target').subrole === 'AXSubrole: AXApplicationGroup' + +PASS successfullyParsed is true + +TEST COMPLETE +Press diff --git a/LayoutTests/accessibility/mac/dynamic-empty-group.html b/LayoutTests/accessibility/mac/dynamic-empty-group.html new file mode 100644 index 0000000000000..90a102f12e2ac --- /dev/null +++ b/LayoutTests/accessibility/mac/dynamic-empty-group.html @@ -0,0 +1,34 @@ + + + + + + + + +
+
+
+
+ + + + + diff --git a/Source/WebCore/accessibility/AXCoreObject.cpp b/Source/WebCore/accessibility/AXCoreObject.cpp index 652a5830ada8e..a55168098e094 100644 --- a/Source/WebCore/accessibility/AXCoreObject.cpp +++ b/Source/WebCore/accessibility/AXCoreObject.cpp @@ -215,6 +215,13 @@ AXCoreObject::AccessibilityChildrenVector AXCoreObject::tabChildren() } #if ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE) +static bool isValidChildForTable(AXCoreObject& object) +{ + auto role = object.roleValue(); + // Tables can only have these roles as exposed-to-AT children. + return role == AccessibilityRole::Row || role == AccessibilityRole::Column || role == AccessibilityRole::TableHeaderContainer || role == AccessibilityRole::Caption; +} + AXCoreObject::AccessibilityChildrenVector AXCoreObject::unignoredChildren(bool updateChildrenIfNeeded) { if (onlyAddsUnignoredChildren()) @@ -227,12 +234,7 @@ AXCoreObject::AccessibilityChildrenVector AXCoreObject::unignoredChildren(bool u const auto& children = childrenIncludingIgnored(updateChildrenIfNeeded); RefPtr descendant = children.size() ? children[0].ptr() : nullptr; while (descendant && descendant != this) { - bool childIsValid = true; - if (isExposedTable) { - auto role = descendant->roleValue(); - // Tables can only have these roles as exposed-to-AT children. - childIsValid = role == AccessibilityRole::Row || role == AccessibilityRole::Column || role == AccessibilityRole::TableHeaderContainer || role == AccessibilityRole::Caption; - } + bool childIsValid = !isExposedTable || isValidChildForTable(*descendant); if (!childIsValid || descendant->isIgnored()) { descendant = descendant->nextInPreOrder(updateChildrenIfNeeded, /* stayWithin */ this); continue; @@ -248,6 +250,23 @@ AXCoreObject::AccessibilityChildrenVector AXCoreObject::unignoredChildren(bool u } return unignoredChildren; } + +AXCoreObject* AXCoreObject::firstUnignoredChild() +{ + const auto& children = childrenIncludingIgnored(/* updateChildrenIfNeeded */ true); + RefPtr descendant = children.size() ? children[0].ptr() : nullptr; + if (onlyAddsUnignoredChildren()) + return descendant.get(); + + bool isExposedTable = isTable() && isExposable(); + while (descendant && descendant != this) { + bool childIsValid = !isExposedTable || isValidChildForTable(*descendant); + if (childIsValid && !descendant->isIgnored()) + return descendant.get(); + descendant = descendant->nextInPreOrder(/* updateChildrenIfNeeded */ true, /* stayWithin */ this); + } + return nullptr; +} #endif // ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE) AXCoreObject* AXCoreObject::nextInPreOrder(bool updateChildrenIfNeeded, AXCoreObject* stayWithin) diff --git a/Source/WebCore/accessibility/AXCoreObject.h b/Source/WebCore/accessibility/AXCoreObject.h index 8d0fd24ff244e..f43a8cfa68a21 100644 --- a/Source/WebCore/accessibility/AXCoreObject.h +++ b/Source/WebCore/accessibility/AXCoreObject.h @@ -895,6 +895,9 @@ class AXCoreObject : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtrchildren(); + return children.size() ? children[0].ptr() : nullptr; + } #endif // ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE) // When ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE) is true, this returns IDs of ignored children. diff --git a/Source/WebCore/accessibility/cocoa/AXCoreObjectCocoa.mm b/Source/WebCore/accessibility/cocoa/AXCoreObjectCocoa.mm index b1f718f9d284c..bceb7ffe31dc8 100644 --- a/Source/WebCore/accessibility/cocoa/AXCoreObjectCocoa.mm +++ b/Source/WebCore/accessibility/cocoa/AXCoreObjectCocoa.mm @@ -263,6 +263,39 @@ static void attributedStringSetStyle(NSMutableAttributedString *attributedString return string; } + +NSArray *renderWidgetChildren(const AXCoreObject& object) +{ + if (LIKELY(!object.isWidget())) + return nil; + + id child = Accessibility::retrieveAutoreleasedValueFromMainThread([object = Ref { object }] () -> RetainPtr { + RefPtr widget = object->widget(); + return widget ? widget->accessibilityObject() : nil; + }); + + if (child) + return @[child]; +ALLOW_DEPRECATED_DECLARATIONS_BEGIN + return [object.platformWidget() accessibilityAttributeValue:NSAccessibilityChildrenAttribute]; +ALLOW_DEPRECATED_DECLARATIONS_END +} + +bool AXCoreObject::isEmptyGroup() +{ +#if ENABLE(MODEL_ELEMENT) + if (UNLIKELY(isModel())) + return false; +#endif + + if (UNLIKELY(isRemoteFrame())) + return false; + + return [rolePlatformString() isEqual:NSAccessibilityGroupRole] + && !firstUnignoredChild() + && ![renderWidgetChildren(*this) count]; +} + #endif // PLATFORM(MAC) } // namespace WebCore diff --git a/Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm b/Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm index 532745b1d4783..c0c3b0e661c1f 100644 --- a/Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm +++ b/Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm @@ -205,43 +205,8 @@ return Accessibility::roleToPlatformString(role); } -static bool isEmptyGroup(AccessibilityObject& object) -{ -#if ENABLE(MODEL_ELEMENT) - if (object.isModel()) - return false; -#endif - - if (object.isRemoteFrame()) - return false; - - return [object.rolePlatformString() isEqual:NSAccessibilityGroupRole] - && object.unignoredChildren().isEmpty() - && ![renderWidgetChildren(object) count]; -} - -NSArray *renderWidgetChildren(const AXCoreObject& object) -{ - if (!object.isWidget()) - return nil; - - id child = Accessibility::retrieveAutoreleasedValueFromMainThread([object = Ref { object }] () -> RetainPtr { - auto* widget = object->widget(); - return widget ? widget->accessibilityObject() : nil; - }); - - if (child) - return @[child]; -ALLOW_DEPRECATED_DECLARATIONS_BEGIN - return [object.platformWidget() accessibilityAttributeValue:NSAccessibilityChildrenAttribute]; -ALLOW_DEPRECATED_DECLARATIONS_END -} - String AccessibilityObject::subrolePlatformString() const { - if (isEmptyGroup(*const_cast(this))) - return NSAccessibilityEmptyGroupSubrole; - if (isSecureField()) return NSAccessibilitySecureTextFieldSubrole; if (isSearchField()) diff --git a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm b/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm index 8268664516738..1962038f7f4e7 100644 --- a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm +++ b/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm @@ -1106,6 +1106,9 @@ - (NSBezierPath *)path static NSString *subroleString(AXCoreObject& backingObject) { + if (backingObject.isEmptyGroup()) + return NSAccessibilityEmptyGroupSubrole; + String subrole = backingObject.subrolePlatformString(); if (!subrole.isEmpty()) return subrole; From 04c766519a71e85e84f1fbb81b469874553eec96 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Sat, 22 Feb 2025 15:45:26 -0800 Subject: [PATCH 073/174] Improve arrayStableSort using Powersort and binary insertion sort https://bugs.webkit.org/show_bug.cgi?id=288130 rdar://145235779 Reviewed by Yusuke Suzuki. Currently, JSC uses mergesort and naive insertion sort to sort a JSArray. We can improve this by implementing Powersort, and updating our insertion sort to use binary search to find the insertion position. tagcloud-SP: average ~3% better (significant with 98% probability) UniPoker: average ~1% better (significant with 98% probability) * JSTests/microbenchmarks/sort-custom-comparator.js: Added. (randArray): (comparator): (iteration.i.comparator): * Source/JavaScriptCore/runtime/StableSort.h: (JSC::arrayInsertionSort): (JSC::mergePowersortRuns): (JSC::arrayStableSort): (JSC::arrayMerge): Deleted. Canonical link: https://commits.webkit.org/290894@main --- .../microbenchmarks/sort-custom-comparator.js | 32 +++ Source/JavaScriptCore/runtime/StableSort.h | 190 ++++++++++++++---- 2 files changed, 185 insertions(+), 37 deletions(-) create mode 100644 JSTests/microbenchmarks/sort-custom-comparator.js diff --git a/JSTests/microbenchmarks/sort-custom-comparator.js b/JSTests/microbenchmarks/sort-custom-comparator.js new file mode 100644 index 0000000000000..c9d65152bb1e1 --- /dev/null +++ b/JSTests/microbenchmarks/sort-custom-comparator.js @@ -0,0 +1,32 @@ + +let arraySize = 20000; + +function randArray(min, max) { + let a = []; + for (let i = 0; i < arraySize; ++i) { + a.push(min + Math.floor(Math.random() * (max - min))); + } + return a; +} + +let comparatorCalls = 0; + +function comparator(a, b) { + ++comparatorCalls; + return (a % 1337) - (b % 1337); +} + +for (let iteration = 0; iteration < 30; ++iteration) { + let arr = randArray(0, 1048576); + let tmp = arr.sort(); + // Partially sorted with HUGE runs + let sorted = tmp.sort(comparator); + for (let i = 0; i < sorted.length - 1; ++i) { + if (comparator(sorted[i], sorted[i+1]) > 0) { + throw new Error("bad"); + } + } +} + +print(comparatorCalls); + diff --git a/Source/JavaScriptCore/runtime/StableSort.h b/Source/JavaScriptCore/runtime/StableSort.h index 07b5eeafad89e..352aaf2511b8b 100644 --- a/Source/JavaScriptCore/runtime/StableSort.h +++ b/Source/JavaScriptCore/runtime/StableSort.h @@ -26,6 +26,7 @@ #pragma once #include "ArgList.h" +#include #include WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN @@ -45,54 +46,47 @@ static ALWAYS_INLINE bool coerceComparatorResultToBoolean(JSGlobalObject* global } template -static ALWAYS_INLINE void arrayInsertionSort(VM& vm, std::span span, const Functor& comparator) +static ALWAYS_INLINE void arrayInsertionSort(VM& vm, std::span span, const Functor& comparator, size_t sortedHeader = 0) { auto scope = DECLARE_THROW_SCOPE(vm); auto* array = span.data(); size_t length = span.size(); - for (size_t i = 1; i < length; ++i) { + for (size_t i = sortedHeader + 1; i < length; ++i) { auto value = array[i]; - size_t j = i; - for (; j > 0; --j) { - auto target = array[j - 1]; + + // [l, r) + size_t left = 0; + size_t right = i; + for (; left < right;) { + size_t m = left + (right - left) / 2; + auto target = array[m]; bool result = comparator(value, target); RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, void()); if (!result) - break; - array[j] = target; + left = m + 1; + else + right = m; } - array[j] = value; + ElementType t = value; + for (size_t j = left; j < i; ++j) + std::swap(array[j], t); + array[i] = t; } } template -static ALWAYS_INLINE void arrayMerge(VM& vm, std::span dst, std::span src, size_t srcIndex, size_t srcEnd, size_t width, const Functor& comparator) +static ALWAYS_INLINE void mergePowersortRuns(VM& vm, std::span dst, std::span src, size_t srcIndex1, size_t srcEnd1, size_t srcIndex2, size_t srcEnd2, const Functor& comparator) { auto scope = DECLARE_THROW_SCOPE(vm); - size_t left = srcIndex; - size_t leftEnd = std::min(left + width, srcEnd); - size_t right = leftEnd; - size_t rightEnd = std::min(right + width, srcEnd); - - if (right >= rightEnd) { - // The right subarray does not exist. Just copy src to dst. - // This is OK to use WTF::copyElements since both src and dst comes from MarkedArgumentBuffer when using this function for EncodedJSValue, - // thus marking happens after suspending the main thread completely. - WTF::copyElements(dst.subspan(left), src.subspan(left, rightEnd - left)); - return; - } + size_t left = srcIndex1; + size_t leftEnd = srcEnd1; + size_t right = srcIndex2; + size_t rightEnd = srcEnd2; - bool result = comparator(src[right], src[right - 1]); - RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, void()); - if (!result) { - // This entire array is already sorted. - // This is OK to use WTF::copyElements since both src and dst comes from MarkedArgumentBuffer when using this function for EncodedJSValue, - // thus marking happens after suspending the main thread completely. - WTF::copyElements(dst.subspan(left), src.subspan(left, rightEnd - left)); - return; - } + ASSERT(leftEnd <= right); + ASSERT(rightEnd <= src.size()); for (size_t dstIndex = left; dstIndex < rightEnd; ++dstIndex) { if (right < rightEnd) { @@ -112,25 +106,147 @@ static ALWAYS_INLINE void arrayMerge(VM& vm, std::span dst, std::sp } } -template +// J. Ian Munro and Sebastian Wild. Nearly-Optimal Mergesorts: Fast, Practical Sorting Methods That +// Optimally Adapt to Existing Runs. In 26th Annual European Symposium on Algorithms (ESA 2018). +// Leibniz International Proceedings in Informatics (LIPIcs), Volume 112, pp. 63:1-63:16, Schloss +// Dagstuhl – Leibniz-Zentrum für Informatik (2018) https://doi.org/10.4230/LIPIcs.ESA.2018.63 + +struct SortedRun { + size_t m_begin; + size_t m_end; +}; + +template static ALWAYS_INLINE std::span arrayStableSort(VM& vm, std::span src, std::span dst, const Functor& comparator) { + constexpr size_t extendRunCutoff = 8; + auto scope = DECLARE_THROW_SCOPE(vm); + const size_t numElements = src.size(); + + if (!numElements) + return src; + + // If the array is small, Powersort probably isn't worth it. Just insertion sort. + if (numElements < extendRunCutoff) { + scope.release(); + arrayInsertionSort(vm, src.subspan(0, src.size()), comparator); + return src; + } + + // power takes in [left, middle-1] and [middle, right] + auto power = [](size_t left, size_t middle, size_t right, size_t n) -> unsigned { + UInt128 n1 = middle - left; + UInt128 n2 = right - middle + 1; + // a and b are 2*midpoints of the two ranges, so always within [0, 2n) + UInt128 a = left * 2 + n1; + UInt128 b = middle * 2 + n2; + // n <= 2^64, so n << 62 <= 2^126 + // n << 62 must be <= 2^126, so a << 62 must be < 2^127. thus, we don't end up with overflow + a <<= 62; + b <<= 62; + + // a is within [0, 2n), so a << 62 is within [0, 2^63 n). Thus, (when we calculate a / n, it must be within [0, 2^63) + UInt128 differingBits = (a / n) ^ (b / n); + ASSERT(!(differingBits >> 64)); + return clz(static_cast(differingBits)); + }; + auto to = dst; auto from = src; - for (size_t i = 0; i < src.size(); i += initialWidth) { - arrayInsertionSort(vm, from.subspan(i, std::min(i + initialWidth, src.size()) - i), comparator); + WTF::copyElements(to, spanConstCast(from)); + + struct PowersortStackEntry { + SortedRun run; + unsigned power; + }; + + WTF::Vector powerstack; + // floor(lg(n)) + 1 + powerstack.reserveCapacity(8 * sizeof(numElements) - WTF::clz(numElements)); + + SortedRun run1 { 0, 0 }; + + // ExtendRunRight(run1.start, n) + while (run1.m_end + 1 < numElements) { + bool result = comparator(from[run1.m_end + 1], from[run1.m_end]); RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, src); + if (result) + break; + ++run1.m_end; } - for (size_t width = initialWidth; width < src.size(); width *= 2) { - for (size_t srcIndex = 0; srcIndex < src.size(); srcIndex += 2 * width) { - arrayMerge(vm, to, spanConstCast(from), srcIndex, src.size(), width, comparator); + if (run1.m_end - run1.m_begin < extendRunCutoff) { + // If the run is too short, insertion sort a bit + auto size = std::min(forceRunLength, numElements - run1.m_begin); + arrayInsertionSort(vm, from.subspan(run1.m_begin, size), comparator, run1.m_end - run1.m_begin); + RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, src); + run1.m_end = run1.m_begin + size - 1; + } + + // See if we can extend the run any more. + while (run1.m_end + 1 < numElements) { + bool result = comparator(from[run1.m_end + 1], from[run1.m_end]); + RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, src); + if (result) + break; + ++run1.m_end; + } + + while (run1.m_end + 1 < numElements) { + SortedRun run2 { run1.m_end + 1, run1.m_end + 1 }; + + // ExtendRunRight(run2.start, n) + while (run2.m_end + 1 < numElements) { + bool result = comparator(from[run2.m_end + 1], from[run2.m_end]); + RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, src); + if (result) + break; + ++run2.m_end; + } + + if (run2.m_end - run2.m_begin < extendRunCutoff) { + // If the run is too short, insertion sort a bit + auto size = std::min(forceRunLength, numElements - run2.m_begin); + arrayInsertionSort(vm, from.subspan(run2.m_begin, size), comparator, run2.m_end - run2.m_begin); + RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, src); + run2.m_end = run2.m_begin + size - 1; + } + + // See if we can extend the run any more. + while (run2.m_end + 1 < numElements) { + bool result = comparator(from[run2.m_end + 1], from[run2.m_end]); RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, src); + if (result) + break; + ++run2.m_end; + } + + unsigned p = power(run1.m_begin, run2.m_begin, run2.m_end, numElements); + while (!powerstack.isEmpty() && powerstack.last().power > p) { + auto rangeToMerge = powerstack.takeLast().run; + ASSERT(rangeToMerge.m_end == run1.m_begin - 1); + + mergePowersortRuns(vm, to, spanConstCast(from), rangeToMerge.m_begin, rangeToMerge.m_end + 1, run1.m_begin, run1.m_end + 1, comparator); + RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, src); + WTF::copyElements(from.subspan(rangeToMerge.m_begin, run1.m_end + 1 - rangeToMerge.m_begin), spanConstCast(to).subspan(rangeToMerge.m_begin, run1.m_end + 1 - rangeToMerge.m_begin)); + run1.m_begin = rangeToMerge.m_begin; } - std::swap(to, from); + + powerstack.append({ run1, p }); + run1 = run2; + } + + while (!powerstack.isEmpty()) { + auto rangeToMerge = powerstack.takeLast().run; + ASSERT(rangeToMerge.m_end == run1.m_begin - 1); + + mergePowersortRuns(vm, to, spanConstCast(from), rangeToMerge.m_begin, rangeToMerge.m_end + 1, run1.m_begin, run1.m_end + 1, comparator); + RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, src); + WTF::copyElements(from.subspan(rangeToMerge.m_begin, run1.m_end + 1 - rangeToMerge.m_begin), spanConstCast(to).subspan(rangeToMerge.m_begin, run1.m_end + 1 - rangeToMerge.m_begin)); + run1.m_begin = rangeToMerge.m_begin; } return from.data() == src.data() ? src : dst; From cbd90d50a0ecf36a64c7ed9aed6cb8ad24f4c194 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Sat, 22 Feb 2025 15:55:10 -0800 Subject: [PATCH 074/174] Address safer cpp failures in WebsiteDataStore https://bugs.webkit.org/show_bug.cgi?id=288272 Reviewed by Geoffrey Garen. * Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations: * Source/WebKit/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations: * Source/WebKit/UIProcess/API/C/WKNotificationManager.cpp: (WKNotificationManagerGetSharedServiceWorkerNotificationManager): * Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm: (+[WKWebsiteDataStore _sharedServiceWorkerNotificationManager]): * Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp: (webkit_web_context_class_init): * Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp: (WebKit::NetworkProcessProxy::processPushMessage): * Source/WebKit/UIProcess/Notifications/WebNotificationManagerProxy.cpp: (WebKit::WebNotificationManagerProxy::serviceWorkerManagerSingleton): (WebKit::WebNotificationManagerProxy::providerDidUpdateNotificationPolicy): (WebKit::WebNotificationManagerProxy::providerDidRemoveNotificationPolicies): (WebKit::WebNotificationManagerProxy::sharedServiceWorkerManager): Deleted. (WebKit::WebNotificationManagerProxy::protectedSharedServiceWorkerManager): Deleted. * Source/WebKit/UIProcess/Notifications/WebNotificationManagerProxy.h: * Source/WebKit/UIProcess/WebProcessProxy.cpp: (WebKit::WebProcessProxy::getNotifications): * Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm: (WebKit::managedDomainQueueSingleton): (WebKit::WebsiteDataStore::fetchAllDataStoreIdentifiers): (WebKit::WebsiteDataStore::removeDataStoreWithIdentifier): (WebKit::WebsiteDataStore::initializeManagedDomains): (WebKit::WebsiteDataStore::ensureManagedDomains const): (WebKit::managedDomainQueue): Deleted. * Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp: (WebKit::WebsiteDataStore::websiteDataStoreIOQueueSingleton): (WebKit::WebsiteDataStore::soAuthorizationCoordinator): (WebKit::WebsiteDataStore::forwardManagedDomainsToITPIfInitialized): (WebKit::WebsiteDataStore::showPersistentNotification): (WebKit::WebsiteDataStore::cancelServiceWorkerNotification): (WebKit::WebsiteDataStore::clearServiceWorkerNotification): (WebKit::WebsiteDataStore::didDestroyServiceWorkerNotification): (WebKit::WebsiteDataStore::openWindowFromServiceWorker): (WebKit::WebsiteDataStore::updateServiceWorkerInspectability): (WebKit::WebsiteDataStore::builtInNotificationsEnabled const): (WebKit::WebsiteDataStore::websiteDataStoreIOQueue): Deleted. * Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h: * Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreClient.h: (WebKit::WebsiteDataStoreClient::didReceiveAuthenticationChallenge): Canonical link: https://commits.webkit.org/290895@main --- .../UncountedCallArgsCheckerExpectations | 3 --- .../UncountedLocalVarsCheckerExpectations | 2 -- .../UIProcess/API/C/WKNotificationManager.cpp | 2 +- .../UIProcess/API/Cocoa/WKWebsiteDataStore.mm | 2 +- .../UIProcess/API/glib/WebKitWebContext.cpp | 2 +- .../UIProcess/Network/NetworkProcessProxy.cpp | 2 +- .../WebNotificationManagerProxy.cpp | 11 +++----- .../WebNotificationManagerProxy.h | 3 +-- Source/WebKit/UIProcess/WebProcessProxy.cpp | 2 +- .../Cocoa/WebsiteDataStoreCocoa.mm | 14 +++++----- .../WebsiteData/WebsiteDataStore.cpp | 26 +++++++++---------- .../UIProcess/WebsiteData/WebsiteDataStore.h | 6 ++--- .../WebsiteData/WebsiteDataStoreClient.h | 2 +- 13 files changed, 33 insertions(+), 44 deletions(-) diff --git a/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations b/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations index 3ac2202398e81..7d02df52db488 100644 --- a/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations +++ b/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations @@ -212,10 +212,7 @@ UIProcess/WebPreferences.h UIProcess/WebProcessActivityState.cpp UIProcess/WebURLSchemeHandler.cpp UIProcess/WebURLSchemeTask.cpp -UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm UIProcess/WebsiteData/WebDeviceOrientationAndMotionAccessController.cpp -UIProcess/WebsiteData/WebsiteDataStore.cpp -UIProcess/WebsiteData/WebsiteDataStoreClient.h UIProcess/mac/CorrectionPanel.mm UIProcess/mac/DisplayCaptureSessionManager.mm UIProcess/mac/UserMediaPermissionRequestProxyMac.mm diff --git a/Source/WebKit/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations b/Source/WebKit/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations index ee68bee997fdc..4ad5d8e215e6c 100644 --- a/Source/WebKit/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations +++ b/Source/WebKit/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations @@ -38,8 +38,6 @@ UIProcess/WebAuthentication/AuthenticatorManager.cpp UIProcess/WebAuthentication/Cocoa/NfcService.mm UIProcess/WebPreferences.cpp UIProcess/WebProcessCache.cpp -UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm -UIProcess/WebsiteData/WebsiteDataStore.cpp UIProcess/mac/DisplayCaptureSessionManager.mm UIProcess/mac/WKImmediateActionController.mm UIProcess/mac/WebContextMenuProxyMac.mm diff --git a/Source/WebKit/UIProcess/API/C/WKNotificationManager.cpp b/Source/WebKit/UIProcess/API/C/WKNotificationManager.cpp index 146cccf84ed2c..1602588a52525 100644 --- a/Source/WebKit/UIProcess/API/C/WKNotificationManager.cpp +++ b/Source/WebKit/UIProcess/API/C/WKNotificationManager.cpp @@ -81,5 +81,5 @@ void WKNotificationManagerProviderDidRemoveNotificationPolicies(WKNotificationMa WKNotificationManagerRef WKNotificationManagerGetSharedServiceWorkerNotificationManager() { - return toAPI(&WebNotificationManagerProxy::sharedServiceWorkerManager()); + return toAPI(&WebNotificationManagerProxy::serviceWorkerManagerSingleton()); } diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm b/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm index 8e57628559f12..97a87094e648a 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm @@ -1058,7 +1058,7 @@ - (_WKWebsiteDataStoreConfiguration *)_configuration + (WKNotificationManagerRef)_sharedServiceWorkerNotificationManager { LOG(Push, "Accessing _sharedServiceWorkerNotificationManager"); - return WebKit::toAPI(&WebKit::WebNotificationManagerProxy::sharedServiceWorkerManager()); + return WebKit::toAPI(&WebKit::WebNotificationManagerProxy::serviceWorkerManagerSingleton()); } - (void)_allowTLSCertificateChain:(NSArray *)certificateChain forHost:(NSString *)host diff --git a/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp b/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp index 2bb3b85fecc39..5479cf466e1ba 100644 --- a/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp +++ b/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp @@ -515,7 +515,7 @@ static void webkit_web_context_class_init(WebKitWebContextClass* webContextClass bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); - s_serviceWorkerNotificationProvider = makeUnique(&WebNotificationManagerProxy::sharedServiceWorkerManager(), nullptr); + s_serviceWorkerNotificationProvider = makeUnique(&WebNotificationManagerProxy::serviceWorkerManagerSingleton(), nullptr); gObjectClass->get_property = webkitWebContextGetProperty; gObjectClass->set_property = webkitWebContextSetProperty; diff --git a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp index 2adc88c448427..35d43c25c3dc6 100644 --- a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp +++ b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp @@ -1800,7 +1800,7 @@ void NetworkProcessProxy::processPushMessage(PAL::SessionID sessionID, const Web permission = PushPermissionState::Prompt; auto permissions = dataStore->client().notificationPermissions(); if (permissions.isEmpty()) - permissions = WebNotificationManagerProxy::protectedSharedServiceWorkerManager()->notificationPermissions(); + permissions = WebNotificationManagerProxy::serviceWorkerManagerSingleton().notificationPermissions(); auto origin = SecurityOriginData::fromURL(pushMessage.registrationURL).toString(); if (auto it = permissions.find(origin); it != permissions.end()) diff --git a/Source/WebKit/UIProcess/Notifications/WebNotificationManagerProxy.cpp b/Source/WebKit/UIProcess/Notifications/WebNotificationManagerProxy.cpp index a6c26f011d2e7..ec37d1329ac64 100644 --- a/Source/WebKit/UIProcess/Notifications/WebNotificationManagerProxy.cpp +++ b/Source/WebKit/UIProcess/Notifications/WebNotificationManagerProxy.cpp @@ -53,18 +53,13 @@ Ref WebNotificationManagerProxy::create(WebProcessP return adoptRef(*new WebNotificationManagerProxy(processPool)); } -WebNotificationManagerProxy& WebNotificationManagerProxy::sharedServiceWorkerManager() +WebNotificationManagerProxy& WebNotificationManagerProxy::serviceWorkerManagerSingleton() { ASSERT(isMainRunLoop()); static NeverDestroyed> sharedManager = adoptRef(*new WebNotificationManagerProxy(nullptr)); return sharedManager->get(); } -Ref WebNotificationManagerProxy::protectedSharedServiceWorkerManager() -{ - return sharedServiceWorkerManager(); -} - WebNotificationManagerProxy::WebNotificationManagerProxy(WebProcessPool* processPool) : WebContextSupplement(processPool) , m_provider(makeUnique()) @@ -340,7 +335,7 @@ void WebNotificationManagerProxy::providerDidUpdateNotificationPolicy(const API: if (originString.isEmpty()) return; - if (this == &sharedServiceWorkerManager()) { + if (this == &serviceWorkerManagerSingleton()) { setPushesAndNotificationsEnabledForOrigin(origin->securityOrigin(), enabled); WebProcessPool::sendToAllRemoteWorkerProcesses(Messages::WebNotificationManager::DidUpdateNotificationDecision(originString, enabled)); return; @@ -356,7 +351,7 @@ void WebNotificationManagerProxy::providerDidRemoveNotificationPolicies(API::Arr if (!size) return; - if (this == &sharedServiceWorkerManager()) { + if (this == &serviceWorkerManagerSingleton()) { removePushSubscriptionsForOrigins(apiArrayToSecurityOrigins(origins)); WebProcessPool::sendToAllRemoteWorkerProcesses(Messages::WebNotificationManager::DidRemoveNotificationDecisions(apiArrayToSecurityOriginStrings(origins))); return; diff --git a/Source/WebKit/UIProcess/Notifications/WebNotificationManagerProxy.h b/Source/WebKit/UIProcess/Notifications/WebNotificationManagerProxy.h index b2e68d6ea141b..b4cf4401acab8 100644 --- a/Source/WebKit/UIProcess/Notifications/WebNotificationManagerProxy.h +++ b/Source/WebKit/UIProcess/Notifications/WebNotificationManagerProxy.h @@ -62,8 +62,7 @@ class WebNotificationManagerProxy : public API::ObjectImpl create(WebProcessPool*); - static WebNotificationManagerProxy& sharedServiceWorkerManager(); - static Ref protectedSharedServiceWorkerManager(); + static WebNotificationManagerProxy& serviceWorkerManagerSingleton(); virtual ~WebNotificationManagerProxy(); diff --git a/Source/WebKit/UIProcess/WebProcessProxy.cpp b/Source/WebKit/UIProcess/WebProcessProxy.cpp index 15cebe342fff5..1ac3aeb446910 100644 --- a/Source/WebKit/UIProcess/WebProcessProxy.cpp +++ b/Source/WebKit/UIProcess/WebProcessProxy.cpp @@ -2809,7 +2809,7 @@ void WebProcessProxy::getNotifications(const URL& registrationURL, const String& return; } - WebNotificationManagerProxy::protectedSharedServiceWorkerManager()->getNotifications(registrationURL, tag, sessionID(), WTFMove(callback)); + WebNotificationManagerProxy::serviceWorkerManagerSingleton().getNotifications(registrationURL, tag, sessionID(), WTFMove(callback)); } void WebProcessProxy::getWebCryptoMasterKey(CompletionHandler>&&)>&& completionHandler) diff --git a/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm b/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm index 3ff311c0393df..f2d8b394de378 100644 --- a/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm +++ b/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm @@ -94,10 +94,10 @@ - (void)fetchAllHistoryWithCompletionHandler:(void (^)(NSSet *urls, NSE #endif #if ENABLE(MANAGED_DOMAINS) -static WorkQueue& managedDomainQueue() +static WorkQueue& managedDomainQueueSingleton() { - static auto& queue = WorkQueue::create("com.apple.WebKit.ManagedDomains"_s).leakRef(); - return queue; + static MainThreadNeverDestroyed> queue = WorkQueue::create("com.apple.WebKit.ManagedDomains"_s); + return queue.get(); } static std::atomic hasInitializedManagedDomains = false; static std::atomic managedKeyExists = false; @@ -298,7 +298,7 @@ static String defaultWebsiteDataStoreRootDirectory() { ASSERT(isMainRunLoop()); - websiteDataStoreIOQueue().dispatch([completionHandler = WTFMove(completionHandler), directory = defaultWebsiteDataStoreRootDirectory().isolatedCopy()]() mutable { + websiteDataStoreIOQueueSingleton().dispatch([completionHandler = WTFMove(completionHandler), directory = defaultWebsiteDataStoreRootDirectory().isolatedCopy()]() mutable { auto identifiers = WTF::compactMap(FileSystem::listDirectory(directory), [](auto&& identifierString) { return WTF::UUID::parse(identifierString); }); @@ -324,7 +324,7 @@ static String defaultWebsiteDataStoreRootDirectory() return completionHandler("Data store is in use (by network process)"_s); } - websiteDataStoreIOQueue().dispatch([completionHandler = WTFMove(completionHandler), identifier, directory = defaultWebsiteDataStoreDirectory(identifier).isolatedCopy()]() mutable { + websiteDataStoreIOQueueSingleton().dispatch([completionHandler = WTFMove(completionHandler), identifier, directory = defaultWebsiteDataStoreDirectory(identifier).isolatedCopy()]() mutable { RetainPtr nsCredentialStorage = adoptNS([[NSURLCredentialStorage alloc] _initWithIdentifier:identifier.toString() private:NO]); auto* credentials = [nsCredentialStorage allCredentials]; for (NSURLProtectionSpace *space in credentials) { @@ -837,7 +837,7 @@ static NavigatingToAppBoundDomain schemeOrDomainIsAppBound(const String& host, c if (hasInitializedManagedDomains && forceReinitialization != ForceReinitialization::Yes) return; - managedDomainQueue().dispatch([forceReinitialization] () mutable { + managedDomainQueueSingleton().dispatch([forceReinitialization] () mutable { if (hasInitializedManagedDomains && forceReinitialization != ForceReinitialization::Yes) return; static const auto maxManagedDomainCount = 10; @@ -906,7 +906,7 @@ static NavigatingToAppBoundDomain schemeOrDomainIsAppBound(const String& host, c // Hopping to the background thread then back to the main thread // ensures that initializeManagedDomains() has finished. - managedDomainQueue().dispatch([protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler)] () mutable { + managedDomainQueueSingleton().dispatch([protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler)] () mutable { RunLoop::protectedMain()->dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)] () mutable { ASSERT(hasInitializedManagedDomains); completionHandler(managedDomains()); diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp index 460b0cafbe6d8..7b9780cb47d82 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp @@ -129,10 +129,10 @@ static String computeMediaKeyFile(const String& mediaKeyDirectory) return FileSystem::pathByAppendingComponent(mediaKeyDirectory, "SecureStop.plist"_s); } -WorkQueue& WebsiteDataStore::websiteDataStoreIOQueue() +WorkQueue& WebsiteDataStore::websiteDataStoreIOQueueSingleton() { - static auto& queue = WorkQueue::create("com.apple.WebKit.WebsiteDataStoreIO"_s).leakRef(); - return queue; + static MainThreadNeverDestroyed> queue = WorkQueue::create("com.apple.WebKit.WebsiteDataStoreIO"_s); + return queue.get(); } void WebsiteDataStore::forEachWebsiteDataStore(NOESCAPE Function&& function) @@ -304,7 +304,7 @@ WebsiteDataStore* WebsiteDataStore::existingDataStoreForSessionID(PAL::SessionID #if HAVE(APP_SSO) SOAuthorizationCoordinator& WebsiteDataStore::soAuthorizationCoordinator(const WebPageProxy& pageProxy) { - RELEASE_ASSERT(pageProxy.preferences().isExtensibleSSOEnabled()); + RELEASE_ASSERT(pageProxy.protectedPreferences()->isExtensibleSSOEnabled()); if (!m_soAuthorizationCoordinator) m_soAuthorizationCoordinator = WTF::makeUnique(); @@ -2564,7 +2564,7 @@ void WebsiteDataStore::forwardManagedDomainsToITPIfInitialized(CompletionHandler store->setManagedDomainsForITP(domains, [callbackAggregator] { }); }; - propagateManagedDomains(globalDefaultDataStore().get(), *managedDomains); + propagateManagedDomains(protectedDefaultDataStore().get(), *managedDomains); for (auto& store : allDataStores().values()) propagateManagedDomains(Ref { store.get() }.ptr(), *managedDomains); @@ -2608,23 +2608,23 @@ bool WebsiteDataStore::showPersistentNotification(IPC::Connection* connection, c if (m_client->showNotification(notificationData)) return true; - return WebNotificationManagerProxy::sharedServiceWorkerManager().showPersistent(*this, connection, notificationData, nullptr); + return WebNotificationManagerProxy::serviceWorkerManagerSingleton().showPersistent(*this, connection, notificationData, nullptr); } void WebsiteDataStore::cancelServiceWorkerNotification(const WTF::UUID& notificationID) { - WebNotificationManagerProxy::sharedServiceWorkerManager().cancel(nullptr, notificationID); + WebNotificationManagerProxy::serviceWorkerManagerSingleton().cancel(nullptr, notificationID); } void WebsiteDataStore::clearServiceWorkerNotification(const WTF::UUID& notificationID) { Vector notifications = { notificationID }; - WebNotificationManagerProxy::sharedServiceWorkerManager().clearNotifications(nullptr, notifications); + WebNotificationManagerProxy::serviceWorkerManagerSingleton().clearNotifications(nullptr, notifications); } void WebsiteDataStore::didDestroyServiceWorkerNotification(const WTF::UUID& notificationID) { - WebNotificationManagerProxy::sharedServiceWorkerManager().didDestroyNotification(nullptr, notificationID); + WebNotificationManagerProxy::serviceWorkerManagerSingleton().didDestroyNotification(nullptr, notificationID); } void WebsiteDataStore::openWindowFromServiceWorker(const String& urlString, const WebCore::SecurityOriginData& serviceWorkerOrigin, CompletionHandler)>&& callback) @@ -2635,7 +2635,7 @@ void WebsiteDataStore::openWindowFromServiceWorker(const String& urlString, cons return; } - if (!newPage->pageLoadState().isLoading()) { + if (!newPage->protectedPageLoadState()->isLoading()) { RELEASE_LOG(Loading, "The WKWebView provided in response to a ServiceWorker openWindow request was not in the loading state"); callback(std::nullopt); return; @@ -2787,8 +2787,8 @@ void WebsiteDataStore::updateServiceWorkerInspectability() bool wasInspectable = m_inspectionForServiceWorkersAllowed; m_inspectionForServiceWorkersAllowed = [&] { #if ENABLE(REMOTE_INSPECTOR) - for (auto& page : m_pages) { - if (page.inspectable()) + for (Ref page : m_pages) { + if (page->inspectable()) return true; } #endif // ENABLE(REMOTE_INSPECTOR) @@ -2894,7 +2894,7 @@ bool WebsiteDataStore::builtInNotificationsEnabled() const return defaultBuiltInNotificationsEnabled(); for (Ref page : m_pages) { - if (page->preferences().builtInNotificationsEnabled()) + if (page->protectedPreferences()->builtInNotificationsEnabled()) return true; } return false; diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h index b495136083818..0439774485211 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h @@ -532,7 +532,7 @@ class WebsiteDataStore : public API::ObjectImpl&&); WebsiteDataStore(); - static WorkQueue& websiteDataStoreIOQueue(); + static WorkQueue& websiteDataStoreIOQueueSingleton(); Ref protectedQueue() const; @@ -586,7 +586,7 @@ class WebsiteDataStore : public API::ObjectImpl m_resolvedDirectories WTF_GUARDED_BY_LOCK(m_resolveDirectoriesLock); FileSystem::Salt m_mediaKeysStorageSalt WTF_GUARDED_BY_LOCK(m_resolveDirectoriesLock); - Ref m_configuration; + const Ref m_configuration; bool m_hasResolvedDirectories { false }; RefPtr m_deviceIdHashSaltStorage; #if ENABLE(ENCRYPTED_MEDIA) @@ -604,7 +604,7 @@ class WebsiteDataStore : public API::ObjectImpl m_statisticsTestingCallback; - Ref m_queue; + const Ref m_queue; #if PLATFORM(COCOA) Vector m_uiProcessCookieStorageIdentifier; diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreClient.h b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreClient.h index 3fe956cbe7483..4b056178bcb2f 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreClient.h +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreClient.h @@ -64,7 +64,7 @@ class WebsiteDataStoreClient { virtual void didReceiveAuthenticationChallenge(Ref&& challenge) { - challenge->listener().completeChallenge(AuthenticationChallengeDisposition::PerformDefaultHandling); + challenge->protectedListener()->completeChallenge(AuthenticationChallengeDisposition::PerformDefaultHandling); } virtual void openWindowFromServiceWorker(const String&, const WebCore::SecurityOriginData&, CompletionHandler&& completionHandler) From 5f42dd57877220f5d9d574e11feb19ecf52212e9 Mon Sep 17 00:00:00 2001 From: Per Arne Vollan Date: Sat, 22 Feb 2025 16:19:16 -0800 Subject: [PATCH 075/174] Remove sandbox extension to Web Inspector daemon in the WebContent process rdar://135814632 https://bugs.webkit.org/show_bug.cgi?id=287347 Reviewed by Chris Dumez. When access to the Web Inspector daemon is blocked in the WebContent process sandbox, we cannot create the Service Worker inspection target in the WebContent process anymore. Instead, we create the inspection target in the UI process. We also create a bidirectional communication channel over IPC between the Service worker in the WebContent process and the new inspection target in the UI process. The new inspection target in the UI process will then communicate with the Web Inspector daemon and enable inspection of the Service Worker. Messages from the Service Worker to the UI process are sent via WebProcessProxy, and messages from the Inspector to the Service Worker are being sent through WebSWContextManagerConnection. * Source/JavaScriptCore/inspector/remote/RemoteInspector.h: * Source/WebCore/workers/service/context/ServiceWorkerInspectorProxy.h: * Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm: (WebKit::WebProcessProxy::createServiceWorkerDebuggable): (WebKit::WebProcessProxy::deleteServiceWorkerDebuggable): (WebKit::WebProcessProxy::sendMessageToInspector): * Source/WebKit/UIProcess/WebProcessProxy.h: * Source/WebKit/UIProcess/WebProcessProxy.messages.in: * Source/WebKit/WebKit.xcodeproj/project.pbxproj: * Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableFrontendChannel.cpp: Added. (WebKit::ServiceWorkerDebuggableFrontendChannel::create): (WebKit::ServiceWorkerDebuggableFrontendChannel::ServiceWorkerDebuggableFrontendChannel): (WebKit::ServiceWorkerDebuggableFrontendChannel::sendMessageToFrontend): * Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableFrontendChannel.h: Added.. * Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableProxy.cpp: Added. (WebKit::ServiceWorkerDebuggableProxy::create): (WebKit::ServiceWorkerDebuggableProxy::ServiceWorkerDebuggableProxy): (WebKit::ServiceWorkerDebuggableProxy::connect): (WebKit::ServiceWorkerDebuggableProxy::disconnect): (WebKit::ServiceWorkerDebuggableProxy::dispatchMessageFromRemote): * Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableProxy.h: Added. * Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp: (WebKit::WebSWContextManagerConnection::installServiceWorker): (WebKit::WebSWContextManagerConnection::terminateWorker): (WebKit::WebSWContextManagerConnection::connectToInspector): (WebKit::WebSWContextManagerConnection::disconnectFromInspector): (WebKit::WebSWContextManagerConnection::dispatchMessageFromInspector): * Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h: * Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in: * Source/WebKit/WebProcess/WebProcess.cpp: (WebKit::WebProcess::initializeWebProcess): * Source/WebKit/WebProcess/com.apple.WebProcess.sb.in: * Tools/MiniBrowser/MiniBrowser.entitlements: Canonical link: https://commits.webkit.org/290896@main --- .../inspector/remote/RemoteInspector.h | 2 +- .../context/ServiceWorkerInspectorProxy.h | 6 +- .../UIProcess/Cocoa/WebProcessProxyCocoa.mm | 39 +++++++++ Source/WebKit/UIProcess/WebProcessProxy.h | 14 +++- .../UIProcess/WebProcessProxy.messages.in | 6 ++ .../WebKit/WebKit.xcodeproj/project.pbxproj | 16 ++++ ...ServiceWorkerDebuggableFrontendChannel.cpp | 52 ++++++++++++ .../ServiceWorkerDebuggableFrontendChannel.h | 54 +++++++++++++ .../ServiceWorkerDebuggableProxy.cpp | 79 +++++++++++++++++++ .../Inspector/ServiceWorkerDebuggableProxy.h | 67 ++++++++++++++++ .../Storage/WebSWContextManagerConnection.cpp | 34 ++++++++ .../Storage/WebSWContextManagerConnection.h | 10 +++ .../WebSWContextManagerConnection.messages.in | 6 ++ Source/WebKit/WebProcess/WebProcess.cpp | 2 +- .../WebProcess/com.apple.WebProcess.sb.in | 2 + Tools/MiniBrowser/MiniBrowser.entitlements | 1 + 16 files changed, 384 insertions(+), 6 deletions(-) create mode 100644 Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableFrontendChannel.cpp create mode 100644 Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableFrontendChannel.h create mode 100644 Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableProxy.cpp create mode 100644 Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableProxy.h diff --git a/Source/JavaScriptCore/inspector/remote/RemoteInspector.h b/Source/JavaScriptCore/inspector/remote/RemoteInspector.h index f8f65fb48de14..671a45d22e90d 100644 --- a/Source/JavaScriptCore/inspector/remote/RemoteInspector.h +++ b/Source/JavaScriptCore/inspector/remote/RemoteInspector.h @@ -136,7 +136,7 @@ class RemoteInspector final void registerTarget(RemoteControllableTarget*); void unregisterTarget(RemoteControllableTarget*); void updateTarget(RemoteControllableTarget*); - void sendMessageToRemote(TargetID, const String& message); + JS_EXPORT_PRIVATE void sendMessageToRemote(TargetID, const String& message); RemoteInspector::Client* client() const { return m_client; } JS_EXPORT_PRIVATE void setClient(RemoteInspector::Client*); diff --git a/Source/WebCore/workers/service/context/ServiceWorkerInspectorProxy.h b/Source/WebCore/workers/service/context/ServiceWorkerInspectorProxy.h index d7be3f3dead8c..79ca0ebdcee0c 100644 --- a/Source/WebCore/workers/service/context/ServiceWorkerInspectorProxy.h +++ b/Source/WebCore/workers/service/context/ServiceWorkerInspectorProxy.h @@ -50,9 +50,9 @@ class ServiceWorkerInspectorProxy { void serviceWorkerTerminated(); - void connectToWorker(Inspector::FrontendChannel&); - void disconnectFromWorker(Inspector::FrontendChannel&); - void sendMessageToWorker(String&&); + WEBCORE_EXPORT void connectToWorker(Inspector::FrontendChannel&); + WEBCORE_EXPORT void disconnectFromWorker(Inspector::FrontendChannel&); + WEBCORE_EXPORT void sendMessageToWorker(String&&); void sendMessageFromWorkerToFrontend(String&&); private: diff --git a/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm index 6a24ab4ade129..7ab3ff7fc8150 100644 --- a/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm +++ b/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm @@ -55,6 +55,7 @@ #if ENABLE(REMOTE_INSPECTOR) #import "WebInspectorUtilities.h" +#import #import #endif @@ -70,6 +71,9 @@ #import "TCCSoftLink.h" #endif +#define MESSAGE_CHECK(assertion) MESSAGE_CHECK_BASE(assertion, connection()) +#define MESSAGE_CHECK_URL(url) MESSAGE_CHECK_BASE(checkURLReceivedFromWebProcess(url), connection()) + namespace WebKit { static const Seconds unexpectedActivityDuration = 10_s; @@ -268,5 +272,40 @@ } #endif // ENABLE(LOGD_BLOCKING_IN_WEBCONTENT) +#if ENABLE(REMOTE_INSPECTOR) +void WebProcessProxy::createServiceWorkerDebuggable(WebCore::ServiceWorkerIdentifier identifier, URL&& url) +{ + MESSAGE_CHECK_URL(url); + RELEASE_LOG(Inspector, "WebProcessProxy::createServiceWorkerDebuggable"); + if (!shouldEnableRemoteInspector()) + return; + Ref serviceWorkerDebuggableProxy = ServiceWorkerDebuggableProxy::create(url.string(), identifier, *this); + serviceWorkerDebuggableProxy->setInspectable(true); + serviceWorkerDebuggableProxy->init(); + m_serviceWorkerDebuggableProxies.add(identifier, serviceWorkerDebuggableProxy); +} + +void WebProcessProxy::deleteServiceWorkerDebuggable(WebCore::ServiceWorkerIdentifier identifier) +{ + RELEASE_LOG(Inspector, "WebProcessProxy::deleteServiceWorkerDebuggable"); + if (!shouldEnableRemoteInspector()) + return; + m_serviceWorkerDebuggableProxies.remove(identifier); +} + +void WebProcessProxy::sendMessageToInspector(WebCore::ServiceWorkerIdentifier identifier, String&& message) +{ + RELEASE_LOG(Inspector, "WebProcessProxy::sendMessageToInspector"); + if (!shouldEnableRemoteInspector()) + return; + if (RefPtr serviceWorkerDebuggableProxy = m_serviceWorkerDebuggableProxies.get(identifier)) { + auto targetID = serviceWorkerDebuggableProxy->targetIdentifier(); + Inspector::RemoteInspector::singleton().sendMessageToRemote(targetID, WTFMove(message)); + } +} +#endif + } +#undef MESSAGE_CHECK_URL +#undef MESSAGE_CHECK diff --git a/Source/WebKit/UIProcess/WebProcessProxy.h b/Source/WebKit/UIProcess/WebProcessProxy.h index ff27c5ad578db..b82965d62515b 100644 --- a/Source/WebKit/UIProcess/WebProcessProxy.h +++ b/Source/WebKit/UIProcess/WebProcessProxy.h @@ -79,6 +79,10 @@ #include "LogStreamIdentifier.h" #endif +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) +#include "ServiceWorkerDebuggableProxy.h" +#endif + namespace API { class Navigation; class PageConfiguration; @@ -672,6 +676,12 @@ class WebProcessProxy final : public AuxiliaryProcessProxy { void setupLogStream(uint32_t pid, IPC::StreamServerConnectionHandle&&, LogStreamIdentifier, CompletionHandler&&); #endif +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) + void createServiceWorkerDebuggable(WebCore::ServiceWorkerIdentifier, URL&&); + void deleteServiceWorkerDebuggable(WebCore::ServiceWorkerIdentifier); + void sendMessageToInspector(WebCore::ServiceWorkerIdentifier, String&& message); +#endif + enum class IsWeak : bool { No, Yes }; template class WeakOrStrongPtr { public: @@ -844,7 +854,9 @@ class WebProcessProxy final : public AuxiliaryProcessProxy { bool m_resourceMonitorRuleListRequestedBySomePage { false }; RefPtr m_resourceMonitorRuleList; #endif - +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) + HashMap> m_serviceWorkerDebuggableProxies; +#endif }; WTF::TextStream& operator<<(WTF::TextStream&, const WebProcessProxy&); diff --git a/Source/WebKit/UIProcess/WebProcessProxy.messages.in b/Source/WebKit/UIProcess/WebProcessProxy.messages.in index 6aab8a3a6da93..9ce6ddb5dce0a 100644 --- a/Source/WebKit/UIProcess/WebProcessProxy.messages.in +++ b/Source/WebKit/UIProcess/WebProcessProxy.messages.in @@ -100,4 +100,10 @@ messages -> WebProcessProxy WantsDispatchMessage { #if ENABLE(LOGD_BLOCKING_IN_WEBCONTENT) SetupLogStream(uint32_t pid, IPC::StreamServerConnectionHandle serverConnection, WebKit::LogStreamIdentifier identifier) -> (IPC::Semaphore streamWakeUpSemaphore, IPC::Semaphore streamClientWaitSemaphore) #endif + +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) + CreateServiceWorkerDebuggable(WebCore::ServiceWorkerIdentifier identifier, URL url) + DeleteServiceWorkerDebuggable(WebCore::ServiceWorkerIdentifier identifier) + SendMessageToInspector(WebCore::ServiceWorkerIdentifier identifier, String message)); +#endif } diff --git a/Source/WebKit/WebKit.xcodeproj/project.pbxproj b/Source/WebKit/WebKit.xcodeproj/project.pbxproj index 963d0784f4c79..6f91c07e92055 100644 --- a/Source/WebKit/WebKit.xcodeproj/project.pbxproj +++ b/Source/WebKit/WebKit.xcodeproj/project.pbxproj @@ -2379,6 +2379,10 @@ E3C6727329D4A3AD00AD4452 /* TextTrackRepresentationCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = E36A00E129CF4EBA00AC4E8A /* TextTrackRepresentationCocoa.h */; }; E3C788F22D035CED00D6C13D /* LogClient.h in Headers */ = {isa = PBXBuildFile; fileRef = E3C788EF2D035CED00D6C13D /* LogClient.h */; }; E3CAAA442413279900CED2E2 /* AccessibilitySupportSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = E3CAAA432413278A00CED2E2 /* AccessibilitySupportSPI.h */; }; + E3D0A9412D62B1C90094538A /* ServiceWorkerDebuggableFrontendChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = E3D0A93F2D62B1C90094538A /* ServiceWorkerDebuggableFrontendChannel.h */; }; + E3D0A9422D62B1C90094538A /* ServiceWorkerDebuggableFrontendChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3D0A9402D62B1C90094538A /* ServiceWorkerDebuggableFrontendChannel.cpp */; }; + E3E6297E2D5FA8F200DAE50C /* ServiceWorkerDebuggableProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = E3E6297C2D5FA8F200DAE50C /* ServiceWorkerDebuggableProxy.h */; }; + E3E6297F2D5FA8F200DAE50C /* ServiceWorkerDebuggableProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3E6297D2D5FA8F200DAE50C /* ServiceWorkerDebuggableProxy.cpp */; }; E3E84BDA2AE0AAE50091B3C2 /* XPCEndpoint.h in Copy Testing Headers */ = {isa = PBXBuildFile; fileRef = C14D306724B794E700480387 /* XPCEndpoint.h */; }; E3E84BDB2AE0AAE50091B3C2 /* XPCEndpointClient.h in Copy Testing Headers */ = {isa = PBXBuildFile; fileRef = C14D306824B794E700480387 /* XPCEndpointClient.h */; }; E3E84BEE2AE1AA5A0091B3C2 /* ExtensionKitSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = E3E84BED2AE1AA5A0091B3C2 /* ExtensionKitSPI.h */; }; @@ -8113,7 +8117,11 @@ E3C788F02D035CED00D6C13D /* LogClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LogClient.cpp; sourceTree = ""; }; E3C788FE2D039A4F00D6C13D /* LogMessages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = LogMessages.in; sourceTree = ""; }; E3CAAA432413278A00CED2E2 /* AccessibilitySupportSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilitySupportSPI.h; sourceTree = ""; }; + E3D0A93F2D62B1C90094538A /* ServiceWorkerDebuggableFrontendChannel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ServiceWorkerDebuggableFrontendChannel.h; sourceTree = ""; }; + E3D0A9402D62B1C90094538A /* ServiceWorkerDebuggableFrontendChannel.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ServiceWorkerDebuggableFrontendChannel.cpp; sourceTree = ""; }; E3DC5B1A2C9AE09700D73BB3 /* LogStreamIdentifier.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LogStreamIdentifier.h; sourceTree = ""; }; + E3E6297C2D5FA8F200DAE50C /* ServiceWorkerDebuggableProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ServiceWorkerDebuggableProxy.h; sourceTree = ""; }; + E3E6297D2D5FA8F200DAE50C /* ServiceWorkerDebuggableProxy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ServiceWorkerDebuggableProxy.cpp; sourceTree = ""; }; E3E84BED2AE1AA5A0091B3C2 /* ExtensionKitSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExtensionKitSPI.h; sourceTree = ""; }; E3EFB02C2550617C003C2F96 /* WebSystemSoundDelegate.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WebSystemSoundDelegate.cpp; sourceTree = ""; }; E3EFB02D2550617C003C2F96 /* WebSystemSoundDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebSystemSoundDelegate.h; sourceTree = ""; }; @@ -13624,6 +13632,10 @@ A55BA8121BA23E05007CD33D /* RemoteWebInspectorUI.cpp */, A55BA8131BA23E05007CD33D /* RemoteWebInspectorUI.h */, A55BA8141BA23E05007CD33D /* RemoteWebInspectorUI.messages.in */, + E3D0A9402D62B1C90094538A /* ServiceWorkerDebuggableFrontendChannel.cpp */, + E3D0A93F2D62B1C90094538A /* ServiceWorkerDebuggableFrontendChannel.h */, + E3E6297D2D5FA8F200DAE50C /* ServiceWorkerDebuggableProxy.cpp */, + E3E6297C2D5FA8F200DAE50C /* ServiceWorkerDebuggableProxy.h */, 1C8E2A1C1277833F00BC7BD0 /* WebInspector.messages.in */, BC111A59112F4FBB00337BAB /* WebInspectorClient.cpp */, BC032D6D10F4378D0058C15A /* WebInspectorClient.h */, @@ -17166,6 +17178,8 @@ E18E6918169B667B009B6670 /* SecItemShimProxyMessages.h in Headers */, 570AB8F320AE3BD700B8BE87 /* SecKeyProxyStore.h in Headers */, 514D9F5719119D35000063A7 /* ServicesController.h in Headers */, + E3D0A9412D62B1C90094538A /* ServiceWorkerDebuggableFrontendChannel.h in Headers */, + E3E6297E2D5FA8F200DAE50C /* ServiceWorkerDebuggableProxy.h in Headers */, 5164658027A9C77400E1F2BA /* ServiceWorkerNotificationHandler.h in Headers */, 3A7D62D629D38DD300D57DAC /* ServiceWorkerStorageManager.h in Headers */, 1AFDE65A1954A42B00C48FFA /* SessionState.h in Headers */, @@ -20069,6 +20083,8 @@ 2DC18FB4218A6E9E0025A88D /* RemoteLayerTreeViews.mm in Sources */, 0701789E23BE9CFC005F0FAA /* RemoteMediaPlayerMIMETypeCache.cpp in Sources */, 7BCF70DE2615D06E00E4FB70 /* ScopedRenderingResourcesRequestCocoa.mm in Sources */, + E3D0A9422D62B1C90094538A /* ServiceWorkerDebuggableFrontendChannel.cpp in Sources */, + E3E6297F2D5FA8F200DAE50C /* ServiceWorkerDebuggableProxy.cpp in Sources */, 93468E6D2714AF88009983E3 /* SharedFileHandleCocoa.cpp in Sources */, 9BF622522C3E8559007F7021 /* SharedPreferencesForWebProcess.cpp in Sources */, 575B1BB923CE9C0B0020639A /* SimulatedInputDispatcher.cpp in Sources */, diff --git a/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableFrontendChannel.cpp b/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableFrontendChannel.cpp new file mode 100644 index 0000000000000..0a5023fd00fb7 --- /dev/null +++ b/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableFrontendChannel.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ServiceWorkerDebuggableFrontendChannel.h" + +#include "MessageSenderInlines.h" +#include "WebProcess.h" +#include "WebProcessProxyMessages.h" + +namespace WebKit { + +WTF_MAKE_TZONE_ALLOCATED_IMPL(ServiceWorkerDebuggableFrontendChannel); + +Ref ServiceWorkerDebuggableFrontendChannel::create(WebCore::ServiceWorkerIdentifier identifier) +{ + return adoptRef(*new ServiceWorkerDebuggableFrontendChannel(identifier)); +} + +ServiceWorkerDebuggableFrontendChannel::ServiceWorkerDebuggableFrontendChannel(WebCore::ServiceWorkerIdentifier identifier) + : m_identifier(identifier) +{ +} + +void ServiceWorkerDebuggableFrontendChannel::sendMessageToFrontend(const String& message) +{ + WebProcess::singleton().send(Messages::WebProcessProxy::SendMessageToInspector(m_identifier, message)); +} + +} // namespace WebKit diff --git a/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableFrontendChannel.h b/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableFrontendChannel.h new file mode 100644 index 0000000000000..652363c006f1e --- /dev/null +++ b/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableFrontendChannel.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2025 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace WebKit { + +class ServiceWorkerDebuggableFrontendChannel final : public ThreadSafeRefCounted, public Inspector::FrontendChannel { + WTF_MAKE_TZONE_ALLOCATED(ServiceWorkerDebuggableFrontendChannel); + WTF_MAKE_NONCOPYABLE(ServiceWorkerDebuggableFrontendChannel); +public: + static Ref create(WebCore::ServiceWorkerIdentifier); + virtual ~ServiceWorkerDebuggableFrontendChannel() = default; + +private: + explicit ServiceWorkerDebuggableFrontendChannel(WebCore::ServiceWorkerIdentifier); + + ConnectionType connectionType() const final { return Inspector::FrontendChannel::ConnectionType::Remote; } + void sendMessageToFrontend(const String& message) final; + + WebCore::ServiceWorkerIdentifier m_identifier; +}; + +} // namespace WebKit diff --git a/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableProxy.cpp b/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableProxy.cpp new file mode 100644 index 0000000000000..b4511a49cd152 --- /dev/null +++ b/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableProxy.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2025 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ServiceWorkerDebuggableProxy.h" + +#include "Logging.h" +#include "WebProcessProxy.h" +#include "WebSWContextManagerConnectionMessages.h" +#include + +#if ENABLE(REMOTE_INSPECTOR) + +#include + +namespace WebKit { + +WTF_MAKE_TZONE_ALLOCATED_IMPL(ServiceWorkerDebuggableProxy); + +using namespace Inspector; + +Ref ServiceWorkerDebuggableProxy::create(const String& url, WebCore::ServiceWorkerIdentifier identifier, WebProcessProxy& webProcessProxy) +{ + return adoptRef(*new ServiceWorkerDebuggableProxy(url, identifier, webProcessProxy)); +} + +ServiceWorkerDebuggableProxy::ServiceWorkerDebuggableProxy(const String& url, WebCore::ServiceWorkerIdentifier identifier, WebProcessProxy& webProcessProxy) + : m_scopeURL(url) + , m_identifier(identifier) + , m_webProcessProxy(webProcessProxy) +{ +} + +void ServiceWorkerDebuggableProxy::connect(FrontendChannel& channel, bool, bool) +{ + RELEASE_LOG(Inspector, "ServiceWorkerDebuggableProxy::connect"); + if (RefPtr webProcessProxy = m_webProcessProxy.get()) + webProcessProxy->send(Messages::WebSWContextManagerConnection::ConnectToInspector(m_identifier), 0); +} + +void ServiceWorkerDebuggableProxy::disconnect(FrontendChannel& channel) +{ + RELEASE_LOG(Inspector, "ServiceWorkerDebuggableProxy::disconnect"); + if (RefPtr webProcessProxy = m_webProcessProxy.get()) + webProcessProxy->send(Messages::WebSWContextManagerConnection::DisconnectFromInspector(m_identifier), 0); +} + +void ServiceWorkerDebuggableProxy::dispatchMessageFromRemote(String&& message) +{ + RELEASE_LOG(Inspector, "ServiceWorkerDebuggableProxy::dispatchMessageFromRemote"); + if (RefPtr webProcessProxy = m_webProcessProxy.get()) + webProcessProxy->send(Messages::WebSWContextManagerConnection::DispatchMessageFromInspector(m_identifier, WTFMove(message)), 0); +} + +} // namespace WebKit + +#endif // ENABLE(REMOTE_INSPECTOR) diff --git a/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableProxy.h b/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableProxy.h new file mode 100644 index 0000000000000..cdb52e2aec40a --- /dev/null +++ b/Source/WebKit/WebProcess/Inspector/ServiceWorkerDebuggableProxy.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2025 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(REMOTE_INSPECTOR) + +#include +#include +#include +#include + + +namespace WebKit { + +class WebProcessProxy; + +class ServiceWorkerDebuggableProxy final : public Inspector::RemoteInspectionTarget { + WTF_MAKE_TZONE_ALLOCATED(ServiceWorkerDebuggableProxy); + WTF_MAKE_NONCOPYABLE(ServiceWorkerDebuggableProxy); +public: + static Ref create(const String& url, WebCore::ServiceWorkerIdentifier, WebProcessProxy&); + ~ServiceWorkerDebuggableProxy() = default; + + Inspector::RemoteControllableTarget::Type type() const final { return Inspector::RemoteControllableTarget::Type::ServiceWorker; } + + String name() const final { return "ServiceWorker"_s; } + String url() const final { return m_scopeURL; } + bool hasLocalDebugger() const final { return false; } + + void connect(Inspector::FrontendChannel&, bool isAutomaticConnection = false, bool immediatelyPause = false) final; + void disconnect(Inspector::FrontendChannel&) final; + void dispatchMessageFromRemote(String&& message) final; + +private: + ServiceWorkerDebuggableProxy(const String& url, WebCore::ServiceWorkerIdentifier, WebProcessProxy&); + + String m_scopeURL; + WebCore::ServiceWorkerIdentifier m_identifier; + WeakPtr m_webProcessProxy; +}; + +} // namespace WebKit + +#endif // ENABLE(REMOTE_INSPECTOR) diff --git a/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp b/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp index 4e134c4353252..2cd985f818773 100644 --- a/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp +++ b/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp @@ -76,6 +76,10 @@ #include #endif +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) +#include "ServiceWorkerDebuggableFrontendChannel.h" +#endif + namespace WebKit { using namespace PAL; using namespace WebCore; @@ -203,6 +207,10 @@ void WebSWContextManagerConnection::installServiceWorker(ServiceWorkerContextDat notificationClient = makeUnique(nullptr); #endif +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) + WebProcess::singleton().send(Messages::WebProcessProxy::CreateServiceWorkerDebuggable(serviceWorkerIdentifier, contextData.registration.scopeURL)); +#endif + auto serviceWorkerThreadProxy = ServiceWorkerThreadProxy::create(Ref { page }, WTFMove(contextData), WTFMove(workerData), WTFMove(effectiveUserAgent), workerThreadMode, WebProcess::singleton().cacheStorageProvider(), WTFMove(notificationClient)); auto workerClient = WebWorkerClient::create(WTFMove(page), serviceWorkerThreadProxy->thread()); @@ -477,6 +485,9 @@ void WebSWContextManagerConnection::setScriptResource(ServiceWorkerIdentifier se void WebSWContextManagerConnection::workerTerminated(ServiceWorkerIdentifier serviceWorkerIdentifier) { RELEASE_LOG(ServiceWorker, "WebSWContextManagerConnection::workerTerminated %" PRIu64, serviceWorkerIdentifier.toUInt64()); +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) + WebProcess::singleton().send(Messages::WebProcessProxy::DeleteServiceWorkerDebuggable(serviceWorkerIdentifier)); +#endif m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::WorkerTerminated(serviceWorkerIdentifier), 0); } @@ -592,4 +603,27 @@ void WebSWContextManagerConnection::removeNavigationFetch(WebCore::SWServerConne }); } +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) +void WebSWContextManagerConnection::connectToInspector(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier) +{ + Ref channel = ServiceWorkerDebuggableFrontendChannel::create(serviceWorkerIdentifier); + m_channels.add(serviceWorkerIdentifier, channel); + if (RefPtr serviceWorkerThreadProxy = SWContextManager::singleton().serviceWorkerThreadProxy(serviceWorkerIdentifier)) + serviceWorkerThreadProxy->inspectorProxy().connectToWorker(channel); +} + +void WebSWContextManagerConnection::disconnectFromInspector(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier) +{ + RefPtr channel = m_channels.take(serviceWorkerIdentifier); + if (RefPtr serviceWorkerThreadProxy = SWContextManager::singleton().serviceWorkerThreadProxy(serviceWorkerIdentifier)) + serviceWorkerThreadProxy->inspectorProxy().disconnectFromWorker(*channel); +} + +void WebSWContextManagerConnection::dispatchMessageFromInspector(WebCore::ServiceWorkerIdentifier identifier, String&& message) +{ + if (RefPtr serviceWorkerThreadProxy = SWContextManager::singleton().serviceWorkerThreadProxy(identifier)) + serviceWorkerThreadProxy->inspectorProxy().sendMessageToWorker(WTFMove(message)); +} +#endif + } // namespace WebCore diff --git a/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h b/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h index 6a99fce0a1233..9e2749de9ec5f 100644 --- a/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h +++ b/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h @@ -58,6 +58,7 @@ struct ServiceWorkerContextData; namespace WebKit { class RemoteWorkerFrameLoaderClient; +class ServiceWorkerDebuggableFrontendChannel; class WebServiceWorkerFetchTaskClient; class WebUserContentController; struct RemoteWorkerInitializationData; @@ -134,6 +135,12 @@ class WebSWContextManagerConnection final : public WebCore::SWContextManager::Co void setRegistrationLastUpdateTime(WebCore::ServiceWorkerRegistrationIdentifier, WallTime); void setRegistrationUpdateViaCache(WebCore::ServiceWorkerRegistrationIdentifier, WebCore::ServiceWorkerUpdateViaCache); +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) + void connectToInspector(WebCore::ServiceWorkerIdentifier); + void disconnectFromInspector(WebCore::ServiceWorkerIdentifier); + void dispatchMessageFromInspector(WebCore::ServiceWorkerIdentifier, String&&); +#endif + Ref m_connectionToNetworkProcess; const WebCore::Site m_site; std::optional m_serviceWorkerPageIdentifier; @@ -150,6 +157,9 @@ class WebSWContextManagerConnection final : public WebCore::SWContextManager::Co using FetchKey = std::pair; HashMap> m_ongoingNavigationFetchTasks WTF_GUARDED_BY_CAPABILITY(m_queue.get()); +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) + HashMap> m_channels; +#endif }; } // namespace WebKit diff --git a/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in b/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in index 917d188a412aa..b25785c14b072 100644 --- a/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in +++ b/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in @@ -58,4 +58,10 @@ messages -> WebSWContextManagerConnection { FireUpdateFoundEvent(WebCore::ServiceWorkerRegistrationIdentifier identifier) SetRegistrationLastUpdateTime(WebCore::ServiceWorkerRegistrationIdentifier identifier, WallTime lastUpdateTime) SetRegistrationUpdateViaCache(WebCore::ServiceWorkerRegistrationIdentifier identifier, enum:uint8_t WebCore::ServiceWorkerUpdateViaCache updateViaCache) + +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) + ConnectToInspector(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier) + DisconnectFromInspector(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier) + DispatchMessageFromInspector(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, String message) +#endif } diff --git a/Source/WebKit/WebProcess/WebProcess.cpp b/Source/WebKit/WebProcess/WebProcess.cpp index 162fc63b3b73a..2b4dc1a8ec58e 100644 --- a/Source/WebKit/WebProcess/WebProcess.cpp +++ b/Source/WebKit/WebProcess/WebProcess.cpp @@ -632,7 +632,7 @@ void WebProcess::initializeWebProcess(WebProcessCreationParameters&& parameters, setEnabledServices(parameters.hasImageServices, parameters.hasSelectionServices, parameters.hasRichContentServices); #endif -#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) +#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA) && !ENABLE(REMOVE_XPC_AND_MACH_SANDBOX_EXTENSIONS_IN_WEBCONTENT) if (std::optional auditToken = protectedParentProcessConnection()->getAuditToken()) { RetainPtr auditData = adoptCF(CFDataCreate(nullptr, (const UInt8*)&*auditToken, sizeof(*auditToken))); Inspector::RemoteInspector::singleton().setParentProcessInformation(legacyPresentingApplicationPID(), auditData); diff --git a/Source/WebKit/WebProcess/com.apple.WebProcess.sb.in b/Source/WebKit/WebProcess/com.apple.WebProcess.sb.in index 31fb983a5279b..e55a73710a839 100644 --- a/Source/WebKit/WebProcess/com.apple.WebProcess.sb.in +++ b/Source/WebKit/WebProcess/com.apple.WebProcess.sb.in @@ -1358,11 +1358,13 @@ (deny mach-lookup (with telemetry-backtrace) (global-name "com.apple.webinspector")) +#if !ENABLE(REMOVE_XPC_AND_MACH_SANDBOX_EXTENSIONS_IN_WEBCONTENT) (with-filter (require-not (state-flag "BlockWebInspectorInWebContentSandbox")) (allow mach-lookup (with telemetry-backtrace) (require-all (extension "com.apple.webkit.extension.mach") (global-name "com.apple.webinspector")))) +#endif (deny mach-lookup (with telemetry-backtrace) (global-name diff --git a/Tools/MiniBrowser/MiniBrowser.entitlements b/Tools/MiniBrowser/MiniBrowser.entitlements index f28830aead50a..384527db10b0f 100644 --- a/Tools/MiniBrowser/MiniBrowser.entitlements +++ b/Tools/MiniBrowser/MiniBrowser.entitlements @@ -23,6 +23,7 @@ com.apple.PIPAgent com.apple.Safari.SafeBrowsing.Service com.apple.WebKit.NetworkingDaemon + com.apple.webinspector com.apple.security.temporary-exception.sbpl From 7ce1585cdfca3841ed6fe9935c731f014423e621 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 22 Feb 2025 16:35:34 -0800 Subject: [PATCH 076/174] Fix size of aggregates indexed by CSSPropertyID https://bugs.webkit.org/show_bug.cgi?id=288168 rdar://145259581 Reviewed by Yusuke Suzuki. CSSProperty::numCSSProperties (generated by process-css-properties.py) is not the *total* number of CSS properties defined in the CSSPropertyID enum. It's rather the number of *real* CSS properties, which excludes CSSPropertyInvalid and CSSPropertyCustom. Hence numCSSProperties is always *smaller* than the largest value in CSSPropertyID enum. Because of the confusing name though, some aggregates (arrays and WTF::BitSet) in the codebase use this value as the size, and index into the aggregate using CSSPropertyID values. Therefore, it's possible to cause an OOB by indexing using a value in CSSPropertyID that's larger than numCSSProperties. This is done in e.g CSSProperty::isColorProperty. Fix this by introducing a new variable, cssPropertyIDEnumValueCount, that accurately describes the number of values defined in CSSPropertyID (which is also the largest value in the enum, plus 1) Then change aggregates to use cssPropertyIDEnumValueCount as the size instead. * Source/WebCore/animation/WebAnimationTypes.h: * Source/WebCore/css/CSSProperty.h: * Source/WebCore/css/process-css-properties.py: (GenerationContext.generate_property_id_bit_set): (GenerateCSSPropertyNames._generate_css_property_names_gperf_prelude): (GenerateCSSPropertyNames): * Source/WebCore/rendering/style/WillChangeData.h: * Source/WebCore/style/StyleBuilderState.h: Canonical link: https://commits.webkit.org/290897@main --- Source/WebCore/animation/WebAnimationTypes.h | 2 +- Source/WebCore/css/CSSProperty.h | 4 ++-- Source/WebCore/css/process-css-properties.py | 20 ++++++++++++++----- .../WebCore/rendering/style/WillChangeData.h | 2 +- Source/WebCore/style/StyleBuilderState.h | 4 ++-- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Source/WebCore/animation/WebAnimationTypes.h b/Source/WebCore/animation/WebAnimationTypes.h index 50d0cbaa21ce1..ec4ad79b0c5a7 100644 --- a/Source/WebCore/animation/WebAnimationTypes.h +++ b/Source/WebCore/animation/WebAnimationTypes.h @@ -110,7 +110,7 @@ constexpr OptionSet transformRelatedAcceleratedProper }; struct CSSPropertiesBitSet { - WTF::BitSet m_properties { }; + WTF::BitSet m_properties { }; }; using TimelineRangeValue = std::variant, RefPtr, String>; diff --git a/Source/WebCore/css/CSSProperty.h b/Source/WebCore/css/CSSProperty.h index 5419fdd339c15..99ace36004e67 100644 --- a/Source/WebCore/css/CSSProperty.h +++ b/Source/WebCore/css/CSSProperty.h @@ -117,8 +117,8 @@ class CSSProperty { return colorProperties.get(propertyId); } - static const WEBCORE_EXPORT WTF::BitSet colorProperties; - static const WEBCORE_EXPORT WTF::BitSet physicalProperties; + static const WEBCORE_EXPORT WTF::BitSet colorProperties; + static const WEBCORE_EXPORT WTF::BitSet physicalProperties; bool operator==(const CSSProperty& other) const { diff --git a/Source/WebCore/css/process-css-properties.py b/Source/WebCore/css/process-css-properties.py index da182d3f0a5ae..de7509914447b 100755 --- a/Source/WebCore/css/process-css-properties.py +++ b/Source/WebCore/css/process-css-properties.py @@ -2413,10 +2413,10 @@ def generate_property_id_switch_function_bool(self, *, to, signature, iterable, to.newline() def generate_property_id_bit_set(self, *, to, name, iterable, mapping_to_property=lambda p: p): - to.write(f"const WTF::BitSet {name} = ([]() -> WTF::BitSet {{") + to.write(f"const WTF::BitSet {name} = ([]() -> WTF::BitSet {{") with to.indent(): - to.write(f"WTF::BitSet result;") + to.write(f"WTF::BitSet result;") for item in iterable: to.write(f"result.set({mapping_to_property(item).id});") @@ -2494,7 +2494,7 @@ def _generate_css_property_names_gperf_prelude(self, *, to): ) to.write_block("""\ - static_assert(numCSSProperties + 1 <= std::numeric_limits::max(), "CSSPropertyID should fit into uint16_t."); + static_assert(cssPropertyIDEnumValueCount <= (std::numeric_limits::max() + 1), "CSSPropertyID should fit into uint16_t."); """) all_computed_property_ids = (f"{property.id}," for property in self.properties_and_descriptors.style_properties.all_computed) @@ -2693,7 +2693,7 @@ def _generate_is_exposed_functions(self, *, to): def _generate_is_inherited_property(self, *, to): all_inherited_and_ids = (f'{"true " if hasattr(property, "inherited") and property.inherited else "false"}, // {property.id}' for property in self.properties_and_descriptors.all_unique) - to.write(f"constexpr bool isInheritedPropertyTable[numCSSProperties + {GenerationContext.number_of_predefined_properties}] = {{") + to.write(f"constexpr bool isInheritedPropertyTable[cssPropertyIDEnumValueCount] = {{") with to.indent(): to.write(f"false, // CSSPropertyID::CSSPropertyInvalid") to.write(f"true , // CSSPropertyID::CSSPropertyCustom") @@ -2703,7 +2703,7 @@ def _generate_is_inherited_property(self, *, to): to.write_block(""" bool CSSProperty::isInheritedProperty(CSSPropertyID id) { - ASSERT(id < firstCSSProperty + numCSSProperties); + ASSERT(id < cssPropertyIDEnumValueCount); ASSERT(id != CSSPropertyID::CSSPropertyInvalid); return isInheritedPropertyTable[id]; } @@ -3014,8 +3014,18 @@ def _generate_css_property_names_h_property_constants(self, *, to): to.write(f"}};") to.newline() + to.write(f"// Enum value of the first \"real\" CSS property, which excludes") + to.write(f"// CSSPropertyInvalid and CSSPropertyCustom.") to.write(f"constexpr uint16_t firstCSSProperty = {first};") + + to.write(f"// Total number of enum values in the CSSPropertyID enum. If making an array") + to.write(f"// that can be indexed into using the enum value, use this as the size.") + to.write(f"constexpr uint16_t cssPropertyIDEnumValueCount = {count};") + + to.write(f"// Number of \"real\" CSS properties. This differs from cssPropertyIDEnumValueCount,") + to.write(f"// as this doesn't consider CSSPropertyInvalid and CSSPropertyCustom.") to.write(f"constexpr uint16_t numCSSProperties = {num};") + to.write(f"constexpr unsigned maxCSSPropertyNameLength = {max_length};") to.write(f"constexpr auto firstTopPriorityProperty = {first_top_priority_property.id};") to.write(f"constexpr auto lastTopPriorityProperty = {last_top_priority_property.id};") diff --git a/Source/WebCore/rendering/style/WillChangeData.h b/Source/WebCore/rendering/style/WillChangeData.h index 3b21fc5a77aa0..12fb1ad0da70f 100644 --- a/Source/WebCore/rendering/style/WillChangeData.h +++ b/Source/WebCore/rendering/style/WillChangeData.h @@ -77,7 +77,7 @@ class WillChangeData : public RefCounted { struct AnimatableFeature { static const int numCSSPropertyIDBits = 14; - static_assert(numCSSProperties < (1 << numCSSPropertyIDBits), "CSSPropertyID should fit in 14_bits"); + static_assert(cssPropertyIDEnumValueCount <= (1 << numCSSPropertyIDBits), "CSSPropertyID should fit in 14 bits"); Feature m_feature { Feature::Property }; unsigned m_cssPropertyID : numCSSPropertyIDBits { CSSPropertyInvalid }; diff --git a/Source/WebCore/style/StyleBuilderState.h b/Source/WebCore/style/StyleBuilderState.h index f6c4834f597b4..cdd4fdeb47068 100644 --- a/Source/WebCore/style/StyleBuilderState.h +++ b/Source/WebCore/style/StyleBuilderState.h @@ -161,8 +161,8 @@ class BuilderState { UncheckedKeyHashSet m_appliedCustomProperties; UncheckedKeyHashSet m_inProgressCustomProperties; UncheckedKeyHashSet m_inCycleCustomProperties; - WTF::BitSet m_inProgressProperties; - WTF::BitSet m_invalidAtComputedValueTimeProperties; + WTF::BitSet m_inProgressProperties; + WTF::BitSet m_invalidAtComputedValueTimeProperties; const PropertyCascade::Property* m_currentProperty { nullptr }; SelectorChecker::LinkMatchMask m_linkMatch { }; From 0b476fcbb873dbc513bdc6ec486c0fc69097b1f4 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Sat, 22 Feb 2025 17:04:57 -0800 Subject: [PATCH 077/174] [fullscreen] Use a single queue for event dispatching https://bugs.webkit.org/show_bug.cgi?id=288292 rdar://145372389 Reviewed by Alex Christensen. https://fullscreen.spec.whatwg.org/#run-the-fullscreen-steps specifies a single queue should be used. This slightly changes the order of change events relative to the error events. Other stylistic changes: - Merge some helper methods back into `dispatchPendingEvents`, since they're only used once after 289984@main. - Rename variables to match spec terminology - Simply check the event type instead of having a separate `shouldNotifyMediaElement` bool argument * LayoutTests/TestExpectations: * LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/element-ready-check-not-allowed-cross-origin.sub-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/element-request-fullscreen-cross-origin.sub-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/fullscreen/model/remove-last-expected.txt: * Source/WebCore/dom/Document.cpp: (WebCore::Document::commonTeardown): * Source/WebCore/dom/FullscreenManager.cpp: (WebCore::FullscreenManager::requestFullscreenForElement): (WebCore::FullscreenManager::dispatchPendingEvents): (WebCore::FullscreenManager::queueFullscreenChangeEventForDocument): (WebCore::FullscreenManager::dispatchEventForNode): Deleted. (WebCore::FullscreenManager::dispatchFullscreenChangeOrErrorEvent): Deleted. (WebCore::FullscreenManager::emptyEventQueue): Deleted. * Source/WebCore/dom/FullscreenManager.h: Canonical link: https://commits.webkit.org/290898@main --- LayoutTests/TestExpectations | 2 - ...-not-allowed-cross-origin.sub-expected.txt | 2 - ...t-fullscreen-cross-origin.sub-expected.txt | 2 - .../fullscreen/model/remove-last-expected.txt | 4 +- Source/WebCore/dom/Document.cpp | 2 +- Source/WebCore/dom/FullscreenManager.cpp | 80 ++++++++----------- Source/WebCore/dom/FullscreenManager.h | 13 ++- 7 files changed, 39 insertions(+), 66 deletions(-) diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations index 274ffe8700063..85ab823dadca2 100644 --- a/LayoutTests/TestExpectations +++ b/LayoutTests/TestExpectations @@ -6317,9 +6317,7 @@ imported/w3c/web-platform-tests/fetch/sec-metadata/redirect/multiple-redirect-sa imported/w3c/web-platform-tests/fetch/sec-metadata/redirect/same-origin-redirect.tentative.https.sub.html [ Skip ] imported/w3c/web-platform-tests/fetch/sec-metadata/track.tentative.https.sub.html [ Skip ] imported/w3c/web-platform-tests/fetch/sec-metadata/window-open.tentative.https.sub.html [ Skip ] -imported/w3c/web-platform-tests/fullscreen/api/element-ready-check-not-allowed-cross-origin.sub.html [ Skip ] imported/w3c/web-platform-tests/fullscreen/api/element-request-fullscreen-cross-origin.sub.html [ Skip ] -imported/w3c/web-platform-tests/fullscreen/api/element-request-fullscreen-timing.html [ Skip ] imported/w3c/web-platform-tests/geolocation/non-fully-active.https.html [ Skip ] webkit.org/b/274950 imported/w3c/web-platform-tests/geolocation/enabled-by-permission-policy-attribute.https.sub.html [ Slow Skip ] webkit.org/b/274950 imported/w3c/web-platform-tests/geolocation/disabled-by-permissions-policy.https.sub.html [ Slow Skip ] diff --git a/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/element-ready-check-not-allowed-cross-origin.sub-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/element-ready-check-not-allowed-cross-origin.sub-expected.txt index 69ea10f44def3..009a3baa37c3c 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/element-ready-check-not-allowed-cross-origin.sub-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/element-ready-check-not-allowed-cross-origin.sub-expected.txt @@ -1,6 +1,4 @@ -Harness Error (FAIL), message = Timeout while running cleanup for test named "Cross-origin element ready check with no allowfullscreen or allow attribute". - FAIL Cross-origin element ready check with no allowfullscreen or allow attribute assert_false: cross-origin-default frame entered fullscreen, but allowfullscreen was not set expected false got true diff --git a/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/element-request-fullscreen-cross-origin.sub-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/element-request-fullscreen-cross-origin.sub-expected.txt index f8cf81d4a2f5a..3621b288e378a 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/element-request-fullscreen-cross-origin.sub-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/fullscreen/api/element-request-fullscreen-cross-origin.sub-expected.txt @@ -1,6 +1,4 @@ -Harness Error (FAIL), message = Timeout while running cleanup for test named "Element#requestFullscreen() works properly with a tree of cross-origin iframes". - PASS Element#requestFullscreen() works properly with a tree of cross-origin iframes diff --git a/LayoutTests/imported/w3c/web-platform-tests/fullscreen/model/remove-last-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fullscreen/model/remove-last-expected.txt index 0c357ae423270..c443f03b9fcf5 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/fullscreen/model/remove-last-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/fullscreen/model/remove-last-expected.txt @@ -1,5 +1,3 @@ -FAIL Remove the last element on the fullscreen element stack assert_equals: expected Document node with 2 children but got Element node
- -
+PASS Remove the last element on the fullscreen element stack diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp index 2b28063c7a5f9..b3859b03630d3 100644 --- a/Source/WebCore/dom/Document.cpp +++ b/Source/WebCore/dom/Document.cpp @@ -913,7 +913,7 @@ void Document::commonTeardown() #if ENABLE(FULLSCREEN_API) if (CheckedPtr fullscreenManager = m_fullscreenManager.get()) - fullscreenManager->emptyEventQueue(); + fullscreenManager->clearPendingEvents(); #endif if (CheckedPtr svgExtensions = svgExtensionsIfExists()) diff --git a/Source/WebCore/dom/FullscreenManager.cpp b/Source/WebCore/dom/FullscreenManager.cpp index 0a57c9ee2fdd0..5c3aabbe641fd 100644 --- a/Source/WebCore/dom/FullscreenManager.cpp +++ b/Source/WebCore/dom/FullscreenManager.cpp @@ -111,7 +111,7 @@ void FullscreenManager::requestFullscreenForElement(Ref&& element, Full return completionHandler(Exception { ExceptionCode::TypeError, message }); ERROR_LOG(identifier, message); if (emitErrorEvent == EmitErrorEvent::Yes) { - m_fullscreenErrorEventTargetQueue.append(WTFMove(element)); + m_pendingEvents.append(std::pair { EventType::Error, WTFMove(element) }); protectedDocument()->scheduleRenderingUpdate(RenderingUpdateStep::Fullscreen); } completionHandler(Exception { ExceptionCode::TypeError, message }); @@ -619,55 +619,45 @@ void FullscreenManager::dispatchPendingEvents() // document will be detached and GC'd. We protect it here to make sure we // can finish the function successfully. Ref protectedDocument(document()); - Deque> changeQueue; - m_fullscreenChangeEventTargetQueue.swap(changeQueue); - Deque> errorQueue; - m_fullscreenErrorEventTargetQueue.swap(errorQueue); - dispatchFullscreenChangeOrErrorEvent(changeQueue, EventType::Change, /* shouldNotifyMediaElement */ true); - dispatchFullscreenChangeOrErrorEvent(errorQueue, EventType::Error, /* shouldNotifyMediaElement */ false); -} + // Steps 1-2: + auto pendingEvents = std::exchange(m_pendingEvents, { }); -void FullscreenManager::dispatchEventForNode(Node& node, EventType eventType) -{ - switch (eventType) { - case EventType::Change: { - node.dispatchEvent(Event::create(eventNames().fullscreenchangeEvent, Event::CanBubble::Yes, Event::IsCancelable::No, Event::IsComposed::Yes)); - bool shouldEmitUnprefixed = !(node.hasEventListeners(eventNames().webkitfullscreenchangeEvent) && node.hasEventListeners(eventNames().fullscreenchangeEvent)) && !(node.document().hasEventListeners(eventNames().webkitfullscreenchangeEvent) && node.document().hasEventListeners(eventNames().fullscreenchangeEvent)); - if (shouldEmitUnprefixed) - node.dispatchEvent(Event::create(eventNames().webkitfullscreenchangeEvent, Event::CanBubble::Yes, Event::IsCancelable::No, Event::IsComposed::Yes)); - break; - } - case EventType::Error: - node.dispatchEvent(Event::create(eventNames().fullscreenerrorEvent, Event::CanBubble::Yes, Event::IsCancelable::No, Event::IsComposed::Yes)); - node.dispatchEvent(Event::create(eventNames().webkitfullscreenerrorEvent, Event::CanBubble::Yes, Event::IsCancelable::No, Event::IsComposed::Yes)); - break; - } -} - -void FullscreenManager::dispatchFullscreenChangeOrErrorEvent(Deque>& queue, EventType eventType, bool shouldNotifyMediaElement) -{ - // Step 3 of https://fullscreen.spec.whatwg.org/#run-the-fullscreen-steps - while (!queue.isEmpty()) { - auto node = queue.takeFirst(); + // Step 3: + while (!pendingEvents.isEmpty()) { + auto [eventType, element] = pendingEvents.takeFirst(); // Gaining or losing fullscreen state may change viewport arguments - node->protectedDocument()->updateViewportArguments(); + element->protectedDocument()->updateViewportArguments(); + if (&element->document() != &document()) + protectedDocument->updateViewportArguments(); #if ENABLE(VIDEO) - if (shouldNotifyMediaElement) { - if (RefPtr mediaElement = dynamicDowncast(node.get())) + if (eventType == EventType::Change) { + if (RefPtr mediaElement = dynamicDowncast(element.get())) mediaElement->enteredOrExitedFullscreen(); } -#else - UNUSED_PARAM(shouldNotifyMediaElement); #endif - // If the element was removed from our tree, also message the documentElement. Since we may - // have a document hierarchy, check that node isn't in another document. - if (!node->isConnected() || &node->document() != &document()) - queue.append(document()); - else - dispatchEventForNode(node.get(), eventType); + // Let target be element if element is connected and its node document is document, and otherwise let target be document. + Ref target = [&]() -> Node& { + if (element->isConnected() && &element->document() == &document()) + return element; + return document(); + }(); + + switch (eventType) { + case EventType::Change: { + target->dispatchEvent(Event::create(eventNames().fullscreenchangeEvent, Event::CanBubble::Yes, Event::IsCancelable::No, Event::IsComposed::Yes)); + bool shouldEmitUnprefixed = !(target->hasEventListeners(eventNames().webkitfullscreenchangeEvent) && target->hasEventListeners(eventNames().fullscreenchangeEvent)) && !(target->document().hasEventListeners(eventNames().webkitfullscreenchangeEvent) && target->document().hasEventListeners(eventNames().fullscreenchangeEvent)); + if (shouldEmitUnprefixed) + target->dispatchEvent(Event::create(eventNames().webkitfullscreenchangeEvent, Event::CanBubble::Yes, Event::IsCancelable::No, Event::IsComposed::Yes)); + break; + } + case EventType::Error: + target->dispatchEvent(Event::create(eventNames().fullscreenerrorEvent, Event::CanBubble::Yes, Event::IsCancelable::No, Event::IsComposed::Yes)); + target->dispatchEvent(Event::create(eventNames().webkitfullscreenerrorEvent, Event::CanBubble::Yes, Event::IsCancelable::No, Event::IsComposed::Yes)); + break; + } } } @@ -678,16 +668,10 @@ void FullscreenManager::queueFullscreenChangeEventForDocument(Document& document ASSERT_NOT_REACHED(); return; } - document.fullscreenManager().addElementToChangeEventQueue(*target); + document.fullscreenManager().queueFullscreenChangeEventForElement(*target); document.scheduleRenderingUpdate(RenderingUpdateStep::Fullscreen); } -void FullscreenManager::emptyEventQueue() -{ - m_fullscreenChangeEventTargetQueue.clear(); - m_fullscreenErrorEventTargetQueue.clear(); -} - // MARK: - Fullscreen animation pseudo-class. bool FullscreenManager::isAnimatingFullscreen() const diff --git a/Source/WebCore/dom/FullscreenManager.h b/Source/WebCore/dom/FullscreenManager.h index 8d2d2237fb30f..4fc171b0dc924 100644 --- a/Source/WebCore/dom/FullscreenManager.h +++ b/Source/WebCore/dom/FullscreenManager.h @@ -90,14 +90,10 @@ class FullscreenManager final : public CanMakeWeakPtr, public WEBCORE_EXPORT bool isAnimatingFullscreen() const; WEBCORE_EXPORT void setAnimatingFullscreen(bool); - void emptyEventQueue(); - protected: friend class Document; - enum class EventType : bool { Change, Error }; - void dispatchFullscreenChangeOrErrorEvent(Deque>&, EventType, bool shouldNotifyMediaElement); - void dispatchEventForNode(Node&, EventType); + void clearPendingEvents() { m_pendingEvents.clear(); } private: #if !RELEASE_LOG_DISABLED @@ -111,13 +107,14 @@ class FullscreenManager final : public CanMakeWeakPtr, public RefPtr protectedMainFrameDocument() { return mainFrameDocument(); } bool didEnterFullscreen(); + + enum class EventType : bool { Change, Error }; static void queueFullscreenChangeEventForDocument(Document&); - void addElementToChangeEventQueue(Node& target) { m_fullscreenChangeEventTargetQueue.append(GCReachableRef(target)); } + void queueFullscreenChangeEventForElement(Element& target) { m_pendingEvents.append({ EventType::Change, GCReachableRef(target) }); } WeakRef m_document; - Deque> m_fullscreenChangeEventTargetQueue; - Deque> m_fullscreenErrorEventTargetQueue; + Deque>> m_pendingEvents; bool m_areKeysEnabledInFullscreen { false }; bool m_isAnimatingFullscreen { false }; From 36514def01eb1550439a9edf6651654ca8967be6 Mon Sep 17 00:00:00 2001 From: Geoffrey Garen Date: Sat, 22 Feb 2025 17:39:02 -0800 Subject: [PATCH 078/174] Some RetainPtr cleanup [v2] https://bugs.webkit.org/show_bug.cgi?id=288291 rdar://145371975 Reviewed by Timothy Hatcher. Fixed a series of bugs where is_convertible_v tests attempting to distinguish id from CFTypeRef were incorrect because they were testing e.g. NSString == id instead of NSString* == id. Added a helper for IsNSType, to simplify is_convertible_v tests. Tightened type requirements so that e.g. RetainPtr is supported but RetainPtr is not. Renamed HelperPtrType to RetainPtrType and pulled it outside the class for clarity. The old model of RetainPtr::HelperPtrType == NSString was a bit mind-bending because * RetainPtr is malformed * NSString is not a pointer type Added #defines for CF_RETURNS_RETAINED and NS_RETURNS_RETAINED, to reduce #ifdefs inside the class. Removed support for ObjC GC because it's the year 2025 and the future is now. * Source/WTF/wtf/RetainPtr.h: (WTF::RetainPtr::fromStorageTypeHelper): (WTF::RetainPtr::autorelease): (WTF::RetainPtr::bridgingAutorelease): (WTF::adoptCF): (WTF::adoptNS): (WTF::retainPtr): Canonical link: https://commits.webkit.org/290899@main --- Source/WTF/wtf/RetainPtr.h | 72 +++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/Source/WTF/wtf/RetainPtr.h b/Source/WTF/wtf/RetainPtr.h index e124e142d9f5a..96333b2875ef0 100644 --- a/Source/WTF/wtf/RetainPtr.h +++ b/Source/WTF/wtf/RetainPtr.h @@ -45,10 +45,18 @@ #define CF_RELEASES_ARGUMENT #endif +#ifndef CF_RETURNS_RETAINED +#define CF_RETURNS_RETAINED +#endif + #ifndef NS_RELEASES_ARGUMENT #define NS_RELEASES_ARGUMENT #endif +#ifndef NS_RETURNS_RETAINED +#define NS_RETURNS_RETAINED +#endif + #ifndef __OBJC__ typedef struct objc_object *id; #endif @@ -62,15 +70,17 @@ typedef struct objc_object *id; namespace WTF { -// Unlike most most of our smart pointers, RetainPtr can take either the pointer type or the pointed-to type, -// so both RetainPtr and RetainPtr will work. +// RetainPtr can point to NS or CF objects, e.g. RetainPtr or RetainPtr. template class RetainPtr; +template constexpr bool IsNSType = std::is_convertible_v; +template using RetainPtrType = std::conditional_t, std::remove_pointer_t, T>; + template constexpr RetainPtr adoptCF(T CF_RELEASES_ARGUMENT) WARN_UNUSED_RETURN; #ifdef __OBJC__ -template RetainPtr::HelperPtrType> adoptNS(T NS_RELEASES_ARGUMENT) WARN_UNUSED_RETURN; +template RetainPtr> adoptNS(T NS_RELEASES_ARGUMENT) WARN_UNUSED_RETURN; #endif template class RetainPtr { @@ -78,12 +88,6 @@ template class RetainPtr { using ValueType = std::remove_pointer_t; using PtrType = ValueType*; -#ifdef __OBJC__ - using HelperPtrType = typename std::conditional_t && !std::is_same_v, std::remove_pointer_t, T>; -#else - using HelperPtrType = PtrType; -#endif - RetainPtr() = default; RetainPtr(PtrType); @@ -101,23 +105,13 @@ template class RetainPtr { void clear(); -#ifdef __OBJC__ - template - std::enable_if_t, PtrType> leakRef() NS_RETURNS_RETAINED WARN_UNUSED_RETURN { - static_assert(std::is_same_v, "explicit specialization not allowed"); + template + std::enable_if_t && std::is_same_v, PtrType> leakRef() NS_RETURNS_RETAINED WARN_UNUSED_RETURN { return fromStorageType(std::exchange(m_ptr, nullptr)); } -#else - template - std::enable_if_t, PtrType> leakRef() CF_RETURNS_RETAINED WARN_UNUSED_RETURN { - static_assert(std::is_same_v, "explicit specialization not allowed"); - return fromStorageType(std::exchange(m_ptr, nullptr)); - } -#endif - template - std::enable_if_t, PtrType> leakRef() CF_RETURNS_RETAINED WARN_UNUSED_RETURN { - static_assert(std::is_same_v, "explicit specialization not allowed"); + template + std::enable_if_t && std::is_same_v, PtrType> leakRef() CF_RETURNS_RETAINED WARN_UNUSED_RETURN { return fromStorageType(std::exchange(m_ptr, nullptr)); } @@ -149,7 +143,7 @@ template class RetainPtr { template friend constexpr RetainPtr adoptCF(U CF_RELEASES_ARGUMENT) WARN_UNUSED_RETURN; #ifdef __OBJC__ - template friend RetainPtr::HelperPtrType> adoptNS(U NS_RELEASES_ARGUMENT) WARN_UNUSED_RETURN; + template friend RetainPtr> adoptNS(U NS_RELEASES_ARGUMENT) WARN_UNUSED_RETURN; #endif private: @@ -158,14 +152,17 @@ template class RetainPtr { static constexpr PtrType checkType(PtrType ptr) { return ptr; } + // ARC will try to retain/release this value, but it looks like a tagged immediate, so retain/release ends up being a no-op -- see _objc_registerTaggedPointerClass. static constexpr PtrType hashTableDeletedValue() { return fromStorageType(reinterpret_cast(-1)); } #ifdef __OBJC__ - template static constexpr std::enable_if_t, PtrType> fromStorageTypeHelper(CFTypeRef ptr) + template + static constexpr std::enable_if_t && std::is_same_v, PtrType> fromStorageTypeHelper(CFTypeRef ptr) { return (__bridge PtrType)const_cast(ptr); } - template static constexpr std::enable_if_t, PtrType> fromStorageTypeHelper(CFTypeRef ptr) + template + static constexpr std::enable_if_t && std::is_same_v, PtrType> fromStorageTypeHelper(CFTypeRef ptr) { return (PtrType)const_cast(ptr); } @@ -183,10 +180,10 @@ template class RetainPtr { CFTypeRef m_ptr { nullptr }; }; -template RetainPtr(T) -> RetainPtr>; +template RetainPtr(T) -> RetainPtr>; // Helper function for creating a RetainPtr using template argument deduction. -template RetainPtr::HelperPtrType> retainPtr(T) WARN_UNUSED_RETURN; +template RetainPtr> retainPtr(T) WARN_UNUSED_RETURN; template inline RetainPtr::~RetainPtr() { @@ -221,7 +218,7 @@ template inline void RetainPtr::clear() template inline auto RetainPtr::autorelease() -> PtrType { #ifdef __OBJC__ - if constexpr (std::is_convertible_v) + if constexpr (IsNSType) return CFBridgingRelease(std::exchange(m_ptr, nullptr)); #endif if (m_ptr) @@ -235,7 +232,7 @@ template inline auto RetainPtr::autorelease() -> PtrType // FIXME: It would be better if we could base the return type on the type that is toll-free bridged with T rather than using id. template inline id RetainPtr::bridgingAutorelease() { - static_assert(!std::is_convertible_v, "Don't use bridgingAutorelease for Objective-C pointer types."); + static_assert(!IsNSType, "Don't use bridgingAutorelease for Objective-C pointer types."); return CFBridgingRelease(leakRef()); } @@ -311,29 +308,24 @@ template constexpr bool operator==(T* a, const RetainPtr template constexpr RetainPtr adoptCF(T CF_RELEASES_ARGUMENT ptr) { #ifdef __OBJC__ - static_assert(!std::is_convertible_v, "Don't use adoptCF with Objective-C pointer types, use adoptNS."); + static_assert(!IsNSType, "Don't use adoptCF with Objective-C pointer types, use adoptNS."); #endif return RetainPtr(ptr, RetainPtr::Adopt); } #ifdef __OBJC__ -template inline RetainPtr::HelperPtrType> adoptNS(T NS_RELEASES_ARGUMENT ptr) +template RetainPtr> adoptNS(T NS_RELEASES_ARGUMENT ptr) { - static_assert(std::is_convertible_v, "Don't use adoptNS with Core Foundation pointer types, use adoptCF."); + static_assert(IsNSType, "Don't use adoptNS with Core Foundation pointer types, use adoptCF."); #if __has_feature(objc_arc) return ptr; -#elif defined(OBJC_NO_GC) - using ReturnType = RetainPtr::HelperPtrType>; - return ReturnType { ptr, ReturnType::Adopt }; #else - RetainPtr::HelperPtrType> result = ptr; - [ptr release]; - return result; + return { ptr, RetainPtr>::Adopt }; #endif } #endif -template inline RetainPtr::HelperPtrType> retainPtr(T ptr) +template inline RetainPtr> retainPtr(T ptr) { return ptr; } From ed0f5af4757894f976e19c3afa63d7542dee9817 Mon Sep 17 00:00:00 2001 From: Alan Baradlay Date: Sat, 22 Feb 2025 19:48:31 -0800 Subject: [PATCH 079/174] [Cleanup] Remove RenderListItem::layout https://bugs.webkit.org/show_bug.cgi?id=288288 Reviewed by Antti Koivisto. 1. Remove layout function. All it does is call base class layout. 2. Do not check map unless style diff is layout (style diffing already checks the map). * Source/WebCore/rendering/RenderListItem.cpp: (WebCore::RenderListItem::styleDidChange): (WebCore::RenderListItem::layout): Deleted. * Source/WebCore/rendering/RenderListItem.h: Canonical link: https://commits.webkit.org/290900@main --- Source/WebCore/rendering/RenderListItem.cpp | 14 ++------------ Source/WebCore/rendering/RenderListItem.h | 1 - 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/Source/WebCore/rendering/RenderListItem.cpp b/Source/WebCore/rendering/RenderListItem.cpp index 7afd754665c65..a37cb7dbabe54 100644 --- a/Source/WebCore/rendering/RenderListItem.cpp +++ b/Source/WebCore/rendering/RenderListItem.cpp @@ -263,18 +263,8 @@ void RenderListItem::styleDidChange(StyleDifference diff, const RenderStyle* old { RenderBlockFlow::styleDidChange(diff, oldStyle); - if (!oldStyle || oldStyle->counterDirectives().map.get("list-item"_s) == style().counterDirectives().map.get("list-item"_s)) - return; - - counterDirectivesChanged(); -} - -void RenderListItem::layout() -{ - StackStats::LayoutCheckPoint layoutCheckPoint; - ASSERT(needsLayout()); - - RenderBlockFlow::layout(); + if (diff == StyleDifference::Layout && oldStyle && oldStyle->counterDirectives().map.get("list-item"_s) != style().counterDirectives().map.get("list-item"_s)) + counterDirectivesChanged(); } void RenderListItem::computePreferredLogicalWidths() diff --git a/Source/WebCore/rendering/RenderListItem.h b/Source/WebCore/rendering/RenderListItem.h index 7d37f04e553c8..be9815fa4f7f6 100644 --- a/Source/WebCore/rendering/RenderListItem.h +++ b/Source/WebCore/rendering/RenderListItem.h @@ -62,7 +62,6 @@ class RenderListItem final : public RenderBlockFlow { void paint(PaintInfo&, const LayoutPoint&) final; void styleDidChange(StyleDifference, const RenderStyle* oldStyle) final; - void layout() final; void computePreferredLogicalWidths() final; From 9035c8b753bb153097bdf8cd05277a97e5f313a0 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Sat, 22 Feb 2025 20:26:04 -0800 Subject: [PATCH 080/174] Address safer cpp failures in RemoteMediaEngineConfigurationFactory.cpp https://bugs.webkit.org/show_bug.cgi?id=288294 Reviewed by Charlie Wolfe. * Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations: * Source/WebKit/WebProcess/GPU/media/RemoteMediaEngineConfigurationFactory.cpp: (WebKit::RemoteMediaEngineConfigurationFactory::createDecodingConfiguration): (WebKit::RemoteMediaEngineConfigurationFactory::createEncodingConfiguration): Canonical link: https://commits.webkit.org/290901@main --- .../SaferCPPExpectations/UncountedCallArgsCheckerExpectations | 1 - .../GPU/media/RemoteMediaEngineConfigurationFactory.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations b/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations index 7d02df52db488..a2fcd8d91a87e 100644 --- a/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations +++ b/Source/WebKit/SaferCPPExpectations/UncountedCallArgsCheckerExpectations @@ -267,7 +267,6 @@ WebProcess/GPU/media/RemoteImageDecoderAVFManager.cpp WebProcess/GPU/media/RemoteLegacyCDM.cpp WebProcess/GPU/media/RemoteLegacyCDMFactory.cpp WebProcess/GPU/media/RemoteLegacyCDMSession.cpp -WebProcess/GPU/media/RemoteMediaEngineConfigurationFactory.cpp WebProcess/GPU/media/RemoteMediaPlayerMIMETypeCache.cpp WebProcess/GPU/media/RemoteMediaPlayerManager.cpp WebProcess/GPU/media/RemoteRemoteCommandListener.cpp diff --git a/Source/WebKit/WebProcess/GPU/media/RemoteMediaEngineConfigurationFactory.cpp b/Source/WebKit/WebProcess/GPU/media/RemoteMediaEngineConfigurationFactory.cpp index 370ed8c25ac76..d00d27039044c 100644 --- a/Source/WebKit/WebProcess/GPU/media/RemoteMediaEngineConfigurationFactory.cpp +++ b/Source/WebKit/WebProcess/GPU/media/RemoteMediaEngineConfigurationFactory.cpp @@ -94,7 +94,7 @@ void RemoteMediaEngineConfigurationFactory::createDecodingConfiguration(MediaDec if (!m_webProcess->mediaPlaybackEnabled()) return callback({ }); - gpuProcessConnection().connection().sendWithAsyncReply(Messages::RemoteMediaEngineConfigurationFactoryProxy::CreateDecodingConfiguration(WTFMove(configuration)), [callback = WTFMove(callback)] (MediaCapabilitiesDecodingInfo&& info) mutable { + gpuProcessConnection().protectedConnection()->sendWithAsyncReply(Messages::RemoteMediaEngineConfigurationFactoryProxy::CreateDecodingConfiguration(WTFMove(configuration)), [callback = WTFMove(callback)] (MediaCapabilitiesDecodingInfo&& info) mutable { callback(WTFMove(info)); }); } @@ -104,7 +104,7 @@ void RemoteMediaEngineConfigurationFactory::createEncodingConfiguration(MediaEnc if (!m_webProcess->mediaPlaybackEnabled()) return callback({ }); - gpuProcessConnection().connection().sendWithAsyncReply(Messages::RemoteMediaEngineConfigurationFactoryProxy::CreateEncodingConfiguration(WTFMove(configuration)), [callback = WTFMove(callback)] (MediaCapabilitiesEncodingInfo&& info) mutable { + gpuProcessConnection().protectedConnection()->sendWithAsyncReply(Messages::RemoteMediaEngineConfigurationFactoryProxy::CreateEncodingConfiguration(WTFMove(configuration)), [callback = WTFMove(callback)] (MediaCapabilitiesEncodingInfo&& info) mutable { callback(WTFMove(info)); }); } From b836cc672c50eb995d6ac6413adbb45a3316c0eb Mon Sep 17 00:00:00 2001 From: David Kilzer Date: Sat, 22 Feb 2025 21:16:25 -0800 Subject: [PATCH 081/174] [bmalloc] Remove workaround for ASan false positive stack-buffer-underflow in pas_large_free_create_merged() Reviewed by Yusuke Suzuki. Remove workaround since WebKit no longer builds with Xcode 13 on macOS 12 Monterey. * Configurations/Sanitizers.xcconfig: (WK_ADDRESS_SANITIZER_OTHER_CFLAGS_YES): (WK_NEEDS_ASAN_USE_AFTER_SCOPE_WORKAROUND_FOR_bmalloc_YES): Delete. (WK_NEEDS_ASAN_USE_AFTER_SCOPE_WORKAROUND): Delete. (WK_XCODE_VERSION_BEFORE_13_3_0800): Delete. (WK_XCODE_VERSION_BEFORE_13_3_0900): Delete. (WK_XCODE_VERSION_BEFORE_13_3_1000): Delete. (WK_XCODE_VERSION_BEFORE_13_3_1100): Delete. (WK_XCODE_VERSION_BEFORE_13_3_1200): Delete. (WK_XCODE_VERSION_BEFORE_13_3_1300): Delete. (WK_XCODE_VERSION_BEFORE_13_3_1300_1300): Delete. (WK_XCODE_VERSION_BEFORE_13_3_1300_1310): Delete. (WK_XCODE_VERSION_BEFORE_13_3_1300_1320): Delete. (WK_XCODE_VERSION_AFTER_13_3_1400): Delete. (WK_XCODE_VERSION_AFTER_13_3_1500): Delete. (WK_XCODE_VERSION_AFTER_13_3_1600): Delete. (WK_XCODE_VERSION_AFTER_13_3_1700): Delete. * Source/bmalloc/libpas/src/libpas/pas_large_free_inlines.h: (pas_large_free_create_merged): Canonical link: https://commits.webkit.org/290902@main --- Configurations/Sanitizers.xcconfig | 21 +------------------ .../src/libpas/pas_large_free_inlines.h | 6 +----- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/Configurations/Sanitizers.xcconfig b/Configurations/Sanitizers.xcconfig index 99a36315996f9..25be668577000 100644 --- a/Configurations/Sanitizers.xcconfig +++ b/Configurations/Sanitizers.xcconfig @@ -64,30 +64,11 @@ WK_ANY_SANITIZER_LDFLAGS_YES = -Wl,-rpath,@executable_path/Frameworks; // Address Sanitizer // Add -fsanitize-address-use-after-return=never to disable ASan's "fake stack" to fix JSC garbage collection. -WK_ADDRESS_SANITIZER_OTHER_CFLAGS_YES = -fsanitize-address-use-after-return=never $(WK_NEEDS_ASAN_USE_AFTER_SCOPE_WORKAROUND_FOR_$(PRODUCT_NAME)_$(WK_NEEDS_ASAN_USE_AFTER_SCOPE_WORKAROUND)); +WK_ADDRESS_SANITIZER_OTHER_CFLAGS_YES = -fsanitize-address-use-after-return=never; WK_ADDRESS_SANITIZER_OTHER_LDFLAGS_YES = -fsanitize-address-use-after-return=never; WK_ADDRESS_SANITIZER_OTHER_CPLUSPLUSFLAGS_YES = -U_LIBCPP_HAS_NO_ASAN; -// Workaround ASan false positive in certain Xcode versions: . -WK_NEEDS_ASAN_USE_AFTER_SCOPE_WORKAROUND_FOR_bmalloc_YES = -DWK_WORKAROUND_RDAR_87613908_ASAN_STACK_USE_AFTER_SCOPE; - -WK_NEEDS_ASAN_USE_AFTER_SCOPE_WORKAROUND = $(WK_NOT_$(WK_OR_$(WK_XCODE_VERSION_BEFORE_13_3_$(XCODE_VERSION_MAJOR))_$(WK_XCODE_VERSION_AFTER_13_3_$(XCODE_VERSION_MAJOR)))); - -WK_XCODE_VERSION_BEFORE_13_3_0800 = YES; -WK_XCODE_VERSION_BEFORE_13_3_0900 = YES; -WK_XCODE_VERSION_BEFORE_13_3_1000 = YES; -WK_XCODE_VERSION_BEFORE_13_3_1100 = YES; -WK_XCODE_VERSION_BEFORE_13_3_1200 = YES; -WK_XCODE_VERSION_BEFORE_13_3_1300 = $(WK_XCODE_VERSION_BEFORE_13_3_1300_$(XCODE_VERSION_MINOR)); -WK_XCODE_VERSION_BEFORE_13_3_1300_1300 = YES; -WK_XCODE_VERSION_BEFORE_13_3_1300_1310 = YES; -WK_XCODE_VERSION_BEFORE_13_3_1300_1320 = YES; -WK_XCODE_VERSION_AFTER_13_3_1400 = YES; -WK_XCODE_VERSION_AFTER_13_3_1500 = YES; -WK_XCODE_VERSION_AFTER_13_3_1600 = YES; -WK_XCODE_VERSION_AFTER_13_3_1700 = YES; - // Undefined Behavior Sanitizer // FIXME: Tune list of Undefined Behavior (UBSan) checkers diff --git a/Source/bmalloc/libpas/src/libpas/pas_large_free_inlines.h b/Source/bmalloc/libpas/src/libpas/pas_large_free_inlines.h index 72c4e78b53a33..cf10d88999695 100644 --- a/Source/bmalloc/libpas/src/libpas/pas_large_free_inlines.h +++ b/Source/bmalloc/libpas/src/libpas/pas_large_free_inlines.h @@ -33,11 +33,7 @@ PAS_BEGIN_EXTERN_C; -static -#ifndef WK_WORKAROUND_RDAR_87613908_ASAN_STACK_USE_AFTER_SCOPE -PAS_ALWAYS_INLINE -#endif -pas_large_free +static PAS_ALWAYS_INLINE pas_large_free pas_large_free_create_merged(pas_large_free left, pas_large_free right, pas_large_free_heap_config* config) From 0dc82e8cdc0995ab208f7cd970a9328fd39c0594 Mon Sep 17 00:00:00 2001 From: Tyler Wilcock Date: Sat, 22 Feb 2025 21:25:14 -0800 Subject: [PATCH 082/174] AX: AccessibilityRenderObject::addChildren() sometimes does not null-check the AXObjectCache before using it, causing crashes https://bugs.webkit.org/show_bug.cgi?id=288238 rdar://145319178 Reviewed by Chris Fleizach. I see some rare crashes in accessibility/add-children-pseudo-element.html and other tests because we aren't null-checking the cache before using it. This commit adds the appropriate checks. * Source/WebCore/accessibility/AccessibilityRenderObject.cpp: (WebCore::AccessibilityRenderObject::addChildren): Canonical link: https://commits.webkit.org/290903@main --- Source/WebCore/accessibility/AccessibilityRenderObject.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp index 0e45fa2860212..ebc05f46e3145 100644 --- a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp @@ -2574,12 +2574,12 @@ void AccessibilityRenderObject::addChildren() // being part of the accessibility tree. RefPtr node = dynamicDowncast(this->node()); auto* element = dynamicDowncast(node.get()); - CheckedPtr cache = axObjectCache(); + WeakPtr cache = axObjectCache(); // ::before and ::after pseudos should be the first and last children of the element // that generates them (rather than being siblings to the generating element). if (RefPtr beforePseudo = element ? element->beforePseudoElement() : nullptr) { - if (RefPtr pseudoObject = cache->getOrCreate(*beforePseudo)) + if (RefPtr pseudoObject = cache ? cache->getOrCreate(*beforePseudo) : nullptr) addChildIfNeeded(*pseudoObject); } @@ -2600,7 +2600,7 @@ void AccessibilityRenderObject::addChildren() } if (RefPtr afterPseudo = element ? element->afterPseudoElement() : nullptr) { - if (RefPtr pseudoObject = cache->getOrCreate(*afterPseudo)) + if (RefPtr pseudoObject = cache ? cache->getOrCreate(*afterPseudo) : nullptr) addChildIfNeeded(*pseudoObject); } #else From 56d65459b823771b022695053f05ce1bfb7b286d Mon Sep 17 00:00:00 2001 From: Tyler Wilcock Date: Sat, 22 Feb 2025 21:29:02 -0800 Subject: [PATCH 083/174] AX: Stop calling updateBackingStore() in AXIsolatedObject::children() for performance reasons https://bugs.webkit.org/show_bug.cgi?id=287862 rdar://145062082 Reviewed by Chris Fleizach. AXIsolatedObject::updateBackingStore() calls AXIsolatedTree::applyPendingChanges(), which takes a lock. This is not ideal to use in a function that is as hot as AXIsolatedObject::children(). Calling updateBackingStore() also requires a strong-ref and strong-deref to |this| in the function, since updateBackingStore() can delete |this|. These refs and derefs, and updateBackingStore show up in samples taken on a popular video website. With this commit, we remove the call to updateBackingStore in AXIsolatedObject::children. We really should only need to call updateBackingStore once for every attribute request, and should do so right at the beginning of the attribute request in the wrapper. This may also be a bit more logical, ensuring each attribute request is served from the same snapshot of the tree. This patch also features several unrelated performance improvements: - Remove updateChildrenIfNecessary from AXIsolatedObject. It's a no-op, but we still incur the cost of a virtual function call in our hottest codepaths (e.g. nextInPreOrder). - Remove unnecessary ref-churn, e.g. creating a Ref just to call Vector::find. - Inline isValid() and isInTextRun() in AXTextMarker::findMarker, our hottest text marker function. - Reduce unnecessary calls to AXTextMarker::runs(). All of these things showed up significantly in samples. * Source/WebCore/accessibility/AXCoreObject.cpp: (WebCore::AXCoreObject::nextInPreOrder): (WebCore::AXCoreObject::previousInPreOrder): (WebCore::AXCoreObject::nextSiblingIncludingIgnored const): (WebCore::AXCoreObject::previousSiblingIncludingIgnored): (WebCore::AXCoreObject::nextUnignoredSibling const): * Source/WebCore/accessibility/AXCoreObject.h: * Source/WebCore/accessibility/AXSearchManager.cpp: (WebCore::appendAccessibilityObject): (WebCore::appendChildrenToArray): * Source/WebCore/accessibility/AXTextMarker.cpp: (WebCore::AXTextMarker::findMarker const): * Source/WebCore/accessibility/AccessibilityObject.h: * Source/WebCore/accessibility/AccessibilityRenderObject.cpp: (WebCore::AccessibilityRenderObject::addNodeOnlyChildren): * Source/WebCore/accessibility/AccessibilityScrollView.cpp: (WebCore::AccessibilityScrollView::removeChildScrollbar): * Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp: (WebCore::AXIsolatedObject::children): (WebCore::AXIsolatedObject::updateChildrenIfNecessary): Deleted. * Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h: * Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm: (-[WebAccessibilityObjectWrapper _accessibilityHitTest:returnPlatformElements:]): Canonical link: https://commits.webkit.org/290904@main --- Source/WebCore/accessibility/AXCoreObject.cpp | 18 ++++++------ Source/WebCore/accessibility/AXCoreObject.h | 1 - .../WebCore/accessibility/AXSearchManager.cpp | 9 ++++-- Source/WebCore/accessibility/AXTextMarker.cpp | 22 +++++++++------ .../accessibility/AccessibilityObject.h | 2 +- .../AccessibilityRenderObject.cpp | 8 ++++-- .../accessibility/AccessibilityScrollView.cpp | 4 ++- .../AccessibilityObjectComponentAtspi.cpp | 3 +- .../isolatedtree/AXIsolatedObject.cpp | 28 +++++-------------- .../isolatedtree/AXIsolatedObject.h | 1 - .../mac/WebAccessibilityObjectWrapperMac.mm | 3 +- 11 files changed, 50 insertions(+), 49 deletions(-) diff --git a/Source/WebCore/accessibility/AXCoreObject.cpp b/Source/WebCore/accessibility/AXCoreObject.cpp index a55168098e094..c3e7d04fa66b5 100644 --- a/Source/WebCore/accessibility/AXCoreObject.cpp +++ b/Source/WebCore/accessibility/AXCoreObject.cpp @@ -271,9 +271,6 @@ AXCoreObject* AXCoreObject::firstUnignoredChild() AXCoreObject* AXCoreObject::nextInPreOrder(bool updateChildrenIfNeeded, AXCoreObject* stayWithin) { - if (updateChildrenIfNeeded) - updateChildrenIfNecessary(); - const auto& children = childrenIncludingIgnored(updateChildrenIfNeeded); if (!children.isEmpty()) { auto role = roleValue(); @@ -299,9 +296,6 @@ AXCoreObject* AXCoreObject::nextInPreOrder(bool updateChildrenIfNeeded, AXCoreOb AXCoreObject* AXCoreObject::previousInPreOrder(bool updateChildrenIfNeeded, AXCoreObject* stayWithin) { - if (updateChildrenIfNeeded) - updateChildrenIfNecessary(); - if (stayWithin == this) return nullptr; @@ -337,7 +331,9 @@ AXCoreObject* AXCoreObject::nextSiblingIncludingIgnored(bool updateChildrenIfNee return nullptr; const auto& siblings = parent->childrenIncludingIgnored(updateChildrenIfNeeded); - size_t indexOfThis = siblings.find(Ref { *this }); + size_t indexOfThis = siblings.findIf([this] (const Ref& object) { + return object.ptr() == this; + }); if (indexOfThis == notFound) return nullptr; @@ -351,7 +347,9 @@ AXCoreObject* AXCoreObject::previousSiblingIncludingIgnored(bool updateChildrenI return nullptr; const auto& siblings = parent->childrenIncludingIgnored(updateChildrenIfNeeded); - size_t indexOfThis = siblings.find(Ref { *this }); + size_t indexOfThis = siblings.findIf([this] (const Ref& object) { + return object.ptr() == this; + }); if (indexOfThis == notFound) return nullptr; @@ -368,7 +366,9 @@ AXCoreObject* AXCoreObject::nextUnignoredSibling(bool updateChildrenIfNeeded, AX if (!parent) return nullptr; const auto& siblings = parent->unignoredChildren(updateChildrenIfNeeded); - size_t indexOfThis = siblings.find(Ref { *this }); + size_t indexOfThis = siblings.findIf([this] (const Ref& object) { + return object.ptr() == this; + }); if (indexOfThis == notFound) return nullptr; diff --git a/Source/WebCore/accessibility/AXCoreObject.h b/Source/WebCore/accessibility/AXCoreObject.h index f43a8cfa68a21..55378243c0364 100644 --- a/Source/WebCore/accessibility/AXCoreObject.h +++ b/Source/WebCore/accessibility/AXCoreObject.h @@ -1282,7 +1282,6 @@ class AXCoreObject : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr childrenIDs(bool updateChildrenIfNeeded = true); - virtual void updateChildrenIfNecessary() = 0; AXCoreObject* nextInPreOrder(bool updateChildrenIfNeeded = true, AXCoreObject* stayWithin = nullptr); AXCoreObject* nextSiblingIncludingIgnored(bool updateChildrenIfNeeded) const; AXCoreObject* nextUnignoredSibling(bool updateChildrenIfNeeded, AXCoreObject* unignoredParent = nullptr) const; diff --git a/Source/WebCore/accessibility/AXSearchManager.cpp b/Source/WebCore/accessibility/AXSearchManager.cpp index 2725fafa39b27..653f8f922991f 100644 --- a/Source/WebCore/accessibility/AXSearchManager.cpp +++ b/Source/WebCore/accessibility/AXSearchManager.cpp @@ -198,7 +198,7 @@ bool AXSearchManager::matchWithResultsLimit(Ref object, const Acce static void appendAccessibilityObject(Ref object, AccessibilityObject::AccessibilityChildrenVector& results) { if (LIKELY(!object->isAttachment())) - results.append(object); + results.append(WTFMove(object)); else { // Find the next descendant of this attachment object so search can continue through frames. Widget* widget = object->widgetForAttachmentView(); @@ -250,7 +250,12 @@ static void appendChildrenToArray(Ref object, bool isForward, RefP startObject = newStartObject; } - size_t searchPosition = startObject ? searchChildren.find(Ref { *startObject }) : notFound; + size_t searchPosition = notFound; + if (startObject) { + searchPosition = searchChildren.findIf([&] (const Ref& object) { + return startObject == object.ptr(); + }); + } if (searchPosition != notFound) { if (isForward) diff --git a/Source/WebCore/accessibility/AXTextMarker.cpp b/Source/WebCore/accessibility/AXTextMarker.cpp index 939e605b9f030..c1c93a82bbe4b 100644 --- a/Source/WebCore/accessibility/AXTextMarker.cpp +++ b/Source/WebCore/accessibility/AXTextMarker.cpp @@ -967,30 +967,35 @@ AXTextMarker AXTextMarker::findMarker(AXDirection direction, CoalesceObjectBreak // - ignoreBRs: In most cases, we want to skip
tags when not in an editable context. This is not true, // for example, when computing text marker indexes. - if (!isValid()) + RefPtr object = isolatedObject(); + if (!object) { + // Equivalent to checking AXTextMarker::isValid, but "inlined" because this function is super hot. return { }; - if (!isInTextRun()) + } + const auto* runs = object->textRuns(); + if (!runs || !runs->size()) { + // Equivalent to checking AXTextMarker::isInTextRun, but "inlined" because this function is super hot. return toTextRunMarker().findMarker(direction, coalesceObjectBreaks, ignoreBRs, stopAtID); - - RefPtr object = isolatedObject(); + } // If the BR isn't in an editable ancestor, we shouldn't be including it (in most cases of findMarker). bool shouldSkipBR = ignoreBRs == IgnoreBRs::Yes && object && object->roleValue() == AccessibilityRole::LineBreak && !object->editableAncestor(); - bool isWithinRunBounds = ((direction == AXDirection::Next && offset() < runs()->totalLength()) || (direction == AXDirection::Previous && offset())); + bool isWithinRunBounds = ((direction == AXDirection::Next && offset() < runs->totalLength()) || (direction == AXDirection::Previous && offset())); if (!shouldSkipBR && isWithinRunBounds) { - if (runs()->containsOnlyASCII) { + if (runs->containsOnlyASCII) { // In the common case where the text-runs only contain ASCII, all we need to do is the move the offset by 1, // which is more efficient than turning the runs into a string and creating a CachedTextBreakIterator. return AXTextMarker { treeID(), objectID(), direction == AXDirection::Next ? offset() + 1 : offset() - 1 }; } - CachedTextBreakIterator iterator(runs()->toString(), { }, TextBreakIterator::CaretMode { }, nullAtom()); + CachedTextBreakIterator iterator(runs->toString(), { }, TextBreakIterator::CaretMode { }, nullAtom()); unsigned newOffset = direction == AXDirection::Next ? iterator.following(offset()).value_or(offset() + 1) : iterator.preceding(offset()).value_or(offset() - 1); return AXTextMarker { treeID(), objectID(), newOffset }; } // offset() pointed to the last character in the given object's runs, so let's traverse to find the next object with runs. - if (RefPtr object = findObjectWithRuns(*isolatedObject(), direction, stopAtID)) { + object = findObjectWithRuns(*object, direction, stopAtID); + if (object) { RELEASE_ASSERT(direction == AXDirection::Next ? object->textRuns()->runLength(0) : object->textRuns()->lastRunLength()); // The startingOffset is used to advance one position farther when we are coalescing object breaks and skipping positions. @@ -1000,7 +1005,6 @@ AXTextMarker AXTextMarker::findMarker(AXDirection direction, CoalesceObjectBreak return AXTextMarker { *object, direction == AXDirection::Next ? startingOffset : object->textRuns()->lastRunLength() - startingOffset }; } - return { }; } diff --git a/Source/WebCore/accessibility/AccessibilityObject.h b/Source/WebCore/accessibility/AccessibilityObject.h index e4cde25203302..46b1c36518737 100644 --- a/Source/WebCore/accessibility/AccessibilityObject.h +++ b/Source/WebCore/accessibility/AccessibilityObject.h @@ -542,7 +542,7 @@ class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtr& child) { + return child.ptr() == childObject; + }); + ++insertionIndex; + } continue; } diff --git a/Source/WebCore/accessibility/AccessibilityScrollView.cpp b/Source/WebCore/accessibility/AccessibilityScrollView.cpp index 3f463910653ef..4edff0922dc75 100644 --- a/Source/WebCore/accessibility/AccessibilityScrollView.cpp +++ b/Source/WebCore/accessibility/AccessibilityScrollView.cpp @@ -177,7 +177,9 @@ void AccessibilityScrollView::removeChildScrollbar(AccessibilityObject* scrollba if (!scrollbar) return; - size_t position = m_children.find(Ref { *scrollbar }); + size_t position = m_children.findIf([&scrollbar] (const Ref& child) { + return child.ptr() == scrollbar; + }); if (position != notFound) { m_children[position]->detachFromParent(); m_children.remove(position); diff --git a/Source/WebCore/accessibility/atspi/AccessibilityObjectComponentAtspi.cpp b/Source/WebCore/accessibility/atspi/AccessibilityObjectComponentAtspi.cpp index abd6468e9a123..9ac2535fa567f 100644 --- a/Source/WebCore/accessibility/atspi/AccessibilityObjectComponentAtspi.cpp +++ b/Source/WebCore/accessibility/atspi/AccessibilityObjectComponentAtspi.cpp @@ -112,7 +112,8 @@ AccessibilityObjectAtspi* AccessibilityObjectAtspi::hitTest(const IntPoint& poin } } - m_coreObject->updateChildrenIfNecessary(); + if (auto* axObject = dynamicDowncast(m_coreObject.get())) + axObject->updateChildrenIfNecessary(); if (auto* coreObject = m_coreObject->accessibilityHitTest(convertedPoint)) return coreObject->wrapper(); diff --git a/Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp b/Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp index e9e85e5f1f06e..f96ad074e3f76 100644 --- a/Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp +++ b/Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp @@ -686,32 +686,18 @@ const AXCoreObject::AccessibilityChildrenVector& AXIsolatedObject::children(bool #elif USE(ATSPI) ASSERT(!isMainThread()); #endif - RefPtr protectedThis; - if (updateChildrenIfNeeded) { - // updateBackingStore can delete `this`, so protect it until the end of this function. - protectedThis = this; - updateBackingStore(); - - if (m_childrenDirty) { - m_children = WTF::compactMap(m_childrenIDs, [&](auto& childID) -> std::optional> { - if (RefPtr child = tree()->objectForID(childID)) - return child.releaseNonNull(); - return std::nullopt; - }); - m_childrenDirty = false; - } + if (updateChildrenIfNeeded && m_childrenDirty) { + m_children = WTF::compactMap(m_childrenIDs, [&](auto& childID) -> std::optional> { + if (RefPtr child = tree()->objectForID(childID)) + return child.releaseNonNull(); + return std::nullopt; + }); + m_childrenDirty = false; ASSERT(m_children.size() == m_childrenIDs.size()); } return m_children; } -void AXIsolatedObject::updateChildrenIfNecessary() -{ - // FIXME: this is a no-op for isolated objects and should be removed from - // the public interface. It is used in the mac implementation of - // [WebAccessibilityObjectWrapper accessibilityHitTest]. -} - void AXIsolatedObject::setSelectedChildren(const AccessibilityChildrenVector& selectedChildren) { ASSERT(selectedChildren.isEmpty() || selectedChildren[0]->isAXIsolatedObjectInstance()); diff --git a/Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h b/Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h index f6ab458c57981..774c7d9077e5e 100644 --- a/Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h +++ b/Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h @@ -348,7 +348,6 @@ class AXIsolatedObject final : public AXCoreObject { void setSelectedChildren(const AccessibilityChildrenVector&) final; AccessibilityChildrenVector visibleChildren() final { return tree()->objectsForIDs(vectorAttributeValue(AXProperty::VisibleChildren)); } void setChildrenIDs(Vector&&); - void updateChildrenIfNecessary() final; bool isDetachedFromParent() final; AXIsolatedObject* liveRegionAncestor(bool excludeIfOff = true) const final { return Accessibility::liveRegionAncestor(*this, excludeIfOff); } const String liveRegionStatus() const final { return stringAttributeValue(AXProperty::LiveRegionStatus); } diff --git a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm b/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm index 1962038f7f4e7..34c5b91643d27 100644 --- a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm +++ b/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm @@ -2055,7 +2055,8 @@ - (id)_accessibilityHitTest:(NSPoint)point returnPlatformElements:(BOOL)returnPl if (!backingObject) return nil; - backingObject->updateChildrenIfNecessary(); + if (auto* axObject = dynamicDowncast(backingObject.get())) + axObject->updateChildrenIfNecessary(); auto* axObject = backingObject->accessibilityHitTest(IntPoint(point)); id hit = nil; From 4a3cd84b09aec2fd6e0349ecbea54411a18f5168 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Sat, 22 Feb 2025 22:31:13 -0800 Subject: [PATCH 084/174] Crash under DocumentThreadableLoader::dataReceived() dereferencing an unset std::optional https://bugs.webkit.org/show_bug.cgi?id=288305 rdar://145354732 Reviewed by Charlie Wolfe. Stop passing the resource load identifier to DocumentThreadableLoader::didReceiveData() since it doesn't use it. We don't always have an identifier and it was causing crashes trying to dereferencing an unset std::optional and for no good reason since the identifier wasn't needed anyway. * Source/WebCore/loader/DocumentThreadableLoader.cpp: (WebCore::DocumentThreadableLoader::dataReceived): (WebCore::DocumentThreadableLoader::didReceiveData): (WebCore::DocumentThreadableLoader::loadRequest): * Source/WebCore/loader/DocumentThreadableLoader.h: Canonical link: https://commits.webkit.org/290905@main --- Source/WebCore/loader/DocumentThreadableLoader.cpp | 6 +++--- Source/WebCore/loader/DocumentThreadableLoader.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/WebCore/loader/DocumentThreadableLoader.cpp b/Source/WebCore/loader/DocumentThreadableLoader.cpp index 9a494ad4920b3..d9e30cc24a86d 100644 --- a/Source/WebCore/loader/DocumentThreadableLoader.cpp +++ b/Source/WebCore/loader/DocumentThreadableLoader.cpp @@ -451,10 +451,10 @@ void DocumentThreadableLoader::didReceiveResponse(ResourceLoaderIdentifier ident void DocumentThreadableLoader::dataReceived(CachedResource& resource, const SharedBuffer& buffer) { ASSERT_UNUSED(resource, &resource == m_resource); - didReceiveData(*m_resource->resourceLoaderIdentifier(), buffer); + didReceiveData(buffer); } -void DocumentThreadableLoader::didReceiveData(ResourceLoaderIdentifier, const SharedBuffer& buffer) +void DocumentThreadableLoader::didReceiveData(const SharedBuffer& buffer) { ASSERT(m_client); @@ -686,7 +686,7 @@ void DocumentThreadableLoader::loadRequest(ResourceRequest&& request, SecurityCh didReceiveResponse(identifier, response); if (data) - didReceiveData(identifier, *data); + didReceiveData(*data); const auto* timing = response.deprecatedNetworkLoadMetricsOrNull(); auto resourceTiming = ResourceTiming::fromSynchronousLoad(requestURL, m_options.initiatorType, loadTiming, timing ? *timing : NetworkLoadMetrics::emptyMetrics(), response, securityOrigin()); diff --git a/Source/WebCore/loader/DocumentThreadableLoader.h b/Source/WebCore/loader/DocumentThreadableLoader.h index 963f6f33e582b..083a389024611 100644 --- a/Source/WebCore/loader/DocumentThreadableLoader.h +++ b/Source/WebCore/loader/DocumentThreadableLoader.h @@ -95,7 +95,7 @@ class CachedRawResource; void notifyFinished(CachedResource&, const NetworkLoadMetrics&, LoadWillContinueInAnotherProcess) override; void didReceiveResponse(ResourceLoaderIdentifier, const ResourceResponse&); - void didReceiveData(ResourceLoaderIdentifier, const SharedBuffer&); + void didReceiveData(const SharedBuffer&); void didFinishLoading(std::optional, const NetworkLoadMetrics&); void didFail(std::optional, const ResourceError&); void makeCrossOriginAccessRequest(ResourceRequest&&); From febcbad98051656212532ede02755f28ebe86848 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sun, 23 Feb 2025 00:13:41 -0800 Subject: [PATCH 085/174] [JSC] Implement `Array.prototype.includes` in C++ https://bugs.webkit.org/show_bug.cgi?id=287693 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed by Yusuke Suzuki. Currently, JSC’s `Array#includes` is implemented in JavaScript, whereas the similar `Array#indexOf` is implemented in C++ and benefits from DFG/FTL optimizations. This patch reimplements `Array#includes` in C++ in the same way as `Array#indexOf`, thereby enabling DFG/FTL support. In most cases, the patch shows performance improvements: ``` TipOfTree Patched array-prototype-includes-double 25.2368+-0.1405 ^ 20.9887+-3.0246 ^ definitely 1.2024x faster array-prototype-includes-int32-from-contiguous 22.4340+-0.4231 ^ 17.1624+-0.2591 ^ definitely 1.3072x faster array-prototype-includes-string 43.4965+-0.6154 ^ 30.8301+-0.7207 ^ definitely 1.4108x faster array-prototype-includes-string-16 73.3458+-0.8209 ^ 51.7629+-0.5639 ^ definitely 1.4170x faster array-prototype-includes-double-from-contiguous 73.3174+-0.9760 ^ 42.4634+-1.0352 ^ definitely 1.7266x faster array-prototype-includes-int32 205.9207+-0.2986 ^ 107.2346+-0.4414 ^ definitely 1.9203x faster array-prototype-includes-contiguous 27.8838+-2.6451 ^ 9.7461+-0.1414 ^ definitely 2.8610x faster array-prototype-includes-bigint 17.6066+-0.2040 ^ 12.2816+-0.1275 ^ definitely 1.4336x faster ``` However, benchmarks that use constant strings experience a performance regression. This is due to the loss of constant folding for the `===` operator that was effective in the JavaScript implementation: ``` TipOfTree Patched array-prototype-includes-string-const 17.8715+-0.0715 ! 24.9579+-0.2986 ! definitely 1.3965x slower array-prototype-includes-string-16-const 17.9026+-0.0907 ! 50.5343+-0.6068 ! definitely 2.8227x slower ``` * Source/JavaScriptCore/builtins/ArrayPrototype.js: (includes): Deleted. * Source/JavaScriptCore/runtime/ArrayPrototype.cpp: (JSC::ArrayPrototype::finishCreation): (JSC::JSC_DEFINE_HOST_FUNCTION): * Source/JavaScriptCore/runtime/CommonIdentifiers.h: * Source/JavaScriptCore/runtime/JSCJSValueInlines.h: (JSC::sameValueZero): Canonical link: https://commits.webkit.org/290906@main --- .../array-prototype-includes-bigint.js | 9 ++ .../array-prototype-includes-contiguous.js | 21 +++ ...ototype-includes-double-from-contiguous.js | 20 +++ .../array-prototype-includes-double.js | 9 ++ ...rototype-includes-int32-from-contiguous.js | 20 +++ .../array-prototype-includes-int32.js | 11 ++ ...rray-prototype-includes-string-16-const.js | 36 +++++ .../array-prototype-includes-string-16.js | 36 +++++ .../array-prototype-includes-string-const.js | 35 +++++ .../array-prototype-includes-string.js | 35 +++++ ...array-prototype-includes-contiguous-nan.js | 10 ++ .../array-prototype-includes-double-nan.js | 10 ++ .../array-prototype-includes-doublerepuse.js | 13 ++ ...rray-prototype-includes-hole-contiguous.js | 10 ++ .../array-prototype-includes-hole-double.js | 10 ++ .../array-prototype-includes-hole-int32.js | 10 ++ .../array-prototype-includes-int32-nan.js | 10 ++ .../array-prototype-includes-int32use.js | 13 ++ .../array-prototype-includes-objectuse.js | 14 ++ .../array-prototype-includes-otheruse.js | 16 +++ .../array-prototype-includes-stringuse.js | 13 ++ .../array-prototype-includes-symboluse.js | 14 ++ ...rototype-includes-untypeduse-contiguous.js | 13 ++ ...ray-prototype-includes-untypeduse-int32.js | 13 ++ .../JavaScriptCore/builtins/ArrayPrototype.js | 34 ----- Source/JavaScriptCore/builtins/BuiltinNames.h | 1 + .../dfg/DFGAbstractInterpreterInlines.h | 4 + .../JavaScriptCore/dfg/DFGByteCodeParser.cpp | 5 +- Source/JavaScriptCore/dfg/DFGClobberize.h | 2 + Source/JavaScriptCore/dfg/DFGDoesGC.cpp | 1 + Source/JavaScriptCore/dfg/DFGFixupPhase.cpp | 31 ++++- Source/JavaScriptCore/dfg/DFGNode.h | 2 + Source/JavaScriptCore/dfg/DFGNodeType.h | 1 + Source/JavaScriptCore/dfg/DFGOperations.cpp | 125 ++++++++++++++++++ Source/JavaScriptCore/dfg/DFGOperations.h | 5 + .../dfg/DFGPredictionPropagationPhase.cpp | 5 + Source/JavaScriptCore/dfg/DFGSafeToExecute.h | 1 + .../JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 114 ++++++++++++---- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h | 2 +- .../dfg/DFGSpeculativeJIT32_64.cpp | 3 +- .../dfg/DFGSpeculativeJIT64.cpp | 3 +- Source/JavaScriptCore/ftl/FTLCapabilities.cpp | 1 + Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp | 44 ++++-- .../JavaScriptCore/runtime/ArrayPrototype.cpp | 51 ++++++- .../runtime/CommonIdentifiers.h | 1 + Source/JavaScriptCore/runtime/Intrinsic.h | 1 + Source/JavaScriptCore/runtime/JSArray.cpp | 85 ++++++++++++ Source/JavaScriptCore/runtime/JSArray.h | 2 + .../runtime/JSCJSValueInlines.h | 22 +++ 49 files changed, 858 insertions(+), 89 deletions(-) create mode 100644 JSTests/microbenchmarks/array-prototype-includes-bigint.js create mode 100644 JSTests/microbenchmarks/array-prototype-includes-contiguous.js create mode 100644 JSTests/microbenchmarks/array-prototype-includes-double-from-contiguous.js create mode 100644 JSTests/microbenchmarks/array-prototype-includes-double.js create mode 100644 JSTests/microbenchmarks/array-prototype-includes-int32-from-contiguous.js create mode 100644 JSTests/microbenchmarks/array-prototype-includes-int32.js create mode 100644 JSTests/microbenchmarks/array-prototype-includes-string-16-const.js create mode 100644 JSTests/microbenchmarks/array-prototype-includes-string-16.js create mode 100644 JSTests/microbenchmarks/array-prototype-includes-string-const.js create mode 100644 JSTests/microbenchmarks/array-prototype-includes-string.js create mode 100644 JSTests/stress/array-prototype-includes-contiguous-nan.js create mode 100644 JSTests/stress/array-prototype-includes-double-nan.js create mode 100644 JSTests/stress/array-prototype-includes-doublerepuse.js create mode 100644 JSTests/stress/array-prototype-includes-hole-contiguous.js create mode 100644 JSTests/stress/array-prototype-includes-hole-double.js create mode 100644 JSTests/stress/array-prototype-includes-hole-int32.js create mode 100644 JSTests/stress/array-prototype-includes-int32-nan.js create mode 100644 JSTests/stress/array-prototype-includes-int32use.js create mode 100644 JSTests/stress/array-prototype-includes-objectuse.js create mode 100644 JSTests/stress/array-prototype-includes-otheruse.js create mode 100644 JSTests/stress/array-prototype-includes-stringuse.js create mode 100644 JSTests/stress/array-prototype-includes-symboluse.js create mode 100644 JSTests/stress/array-prototype-includes-untypeduse-contiguous.js create mode 100644 JSTests/stress/array-prototype-includes-untypeduse-int32.js diff --git a/JSTests/microbenchmarks/array-prototype-includes-bigint.js b/JSTests/microbenchmarks/array-prototype-includes-bigint.js new file mode 100644 index 0000000000000..9ebd06001a4d5 --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-bigint.js @@ -0,0 +1,9 @@ +for (var i = 0; i < 1e5; ++i) { + [ + 0n,1n,2n,3n,4n,5n,6n,7n,8n,9n,10n,11n,12n,13n,14n,15n, + 31n,30n,29n,28n,27n,26n,25n,24n,23n,22n,21n,20n,19n, + 18n,17n,16n,32n,33n,34n,35n,36n,37n,38n,39n,40n,41n, + 42n,43n,44n,45n,46n,47n,63n,62n,61n,60n,59n,58n,57n, + 56n,55n,54n,53n,52n,51n,50n,49n, + ].includes(38n); +} diff --git a/JSTests/microbenchmarks/array-prototype-includes-contiguous.js b/JSTests/microbenchmarks/array-prototype-includes-contiguous.js new file mode 100644 index 0000000000000..6801eed8d943a --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-contiguous.js @@ -0,0 +1,21 @@ +const search = { value: 36 }; +const array = [ + { value: 0 }, { value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, + { value: 5 }, { value: 6 }, { value: 7 }, { value: 8 }, { value: 9 }, + { value: 10 }, { value: 11 }, { value: 12 }, { value: 13 }, { value: 14 }, + { value: 15 }, { value: 31 }, { value: 30 }, { value: 29 }, { value: 28 }, + { value: 27 }, { value: 26 }, { value: 25 }, { value: 24 }, { value: 23 }, + { value: 22 }, { value: 21 }, { value: 20 }, { value: 19 }, { value: 18 }, + { value: 17 }, { value: 16 }, { value: 32 }, { value: 33 }, { value: 34 }, + { value: 35 }, search, { value: 37 }, { value: 38 }, { value: 39 }, + { value: 40 }, { value: 41 }, { value: 42 }, { value: 43 }, { value: 44 }, + { value: 45 }, { value: 46 }, { value: 47 }, { value: 63 }, { value: 62 }, + { value: 61 }, { value: 60 }, { value: 59 }, { value: 58 }, { value: 57 }, + { value: 56 }, { value: 55 }, { value: 54 }, { value: 53 }, { value: 52 }, + { value: 51 }, { value: 50 }, { value: 49 }, +]; + +for (var i = 0; i < 1e6; ++i) { + array.includes(search); +} + diff --git a/JSTests/microbenchmarks/array-prototype-includes-double-from-contiguous.js b/JSTests/microbenchmarks/array-prototype-includes-double-from-contiguous.js new file mode 100644 index 0000000000000..ada96523e6f5d --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-double-from-contiguous.js @@ -0,0 +1,20 @@ +const array = [ + { value: 0 }, { value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, + { value: 5 }, { value: 6 }, { value: 7 }, { value: 8 }, { value: 9 }, + { value: 10 }, { value: 11 }, { value: 12 }, { value: 13 }, { value: 14 }, + { value: 15 }, { value: 31 }, { value: 30 }, { value: 29 }, { value: 28 }, + { value: 27 }, { value: 26 }, { value: 25 }, { value: 24 }, { value: 23 }, + { value: 22 }, { value: 21 }, { value: 20 }, { value: 19 }, { value: 18 }, + { value: 17 }, { value: 16 }, { value: 32 }, { value: 33 }, { value: 34 }, + { value: 35 }, 3.6, { value: 37 }, { value: 38 }, { value: 39 }, + { value: 40 }, { value: 41 }, { value: 42 }, { value: 43 }, { value: 44 }, + { value: 45 }, { value: 46 }, { value: 47 }, { value: 63 }, { value: 62 }, + { value: 61 }, { value: 60 }, { value: 59 }, { value: 58 }, { value: 57 }, + { value: 56 }, { value: 55 }, { value: 54 }, { value: 53 }, { value: 52 }, + { value: 51 }, { value: 50 }, { value: 49 }, +]; + +for (var i = 0; i < 1e6; ++i) { + array.includes(3.6); +} + diff --git a/JSTests/microbenchmarks/array-prototype-includes-double.js b/JSTests/microbenchmarks/array-prototype-includes-double.js new file mode 100644 index 0000000000000..f96e7ab91946c --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-double.js @@ -0,0 +1,9 @@ +for (var i = 0; i < 1e6; ++i) { + [ + 0.2, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 7.2, 8.2, 9.2, 10.2, 11.2, 12.2, 13.2, 14.2, 15.2, + 31.2, 30.2, 29.2, 28.2, 27.2, 26.2, 25.2, 24.2, 23.2, 22.2, 21.2, 20.2, 19.2, 18.2, + 17.2, 16.2, 32.2, 33.2, 34.2, 35.2, 36.2, 37.2, 38.2, 39.2, 40.2, 41.2, 42.2, 43.2, + 44.2, 45.2, 46.2, 47.2, 63.2, 62.2, 61.2, 60.2, 59.2, 58.2, 57.2, 56.2, 55.2, 54.2, + 53.2, 52.2, 51.2, 50.2, 49.2, + ].includes(38.2); +} diff --git a/JSTests/microbenchmarks/array-prototype-includes-int32-from-contiguous.js b/JSTests/microbenchmarks/array-prototype-includes-int32-from-contiguous.js new file mode 100644 index 0000000000000..24c8fd3413bdc --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-int32-from-contiguous.js @@ -0,0 +1,20 @@ +const array = [ + { value: 0 }, { value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, + { value: 5 }, { value: 6 }, { value: 7 }, { value: 8 }, { value: 9 }, + { value: 10 }, { value: 11 }, { value: 12 }, { value: 13 }, { value: 14 }, + { value: 15 }, { value: 31 }, { value: 30 }, { value: 29 }, { value: 28 }, + { value: 27 }, { value: 26 }, { value: 25 }, { value: 24 }, { value: 23 }, + { value: 22 }, { value: 21 }, { value: 20 }, { value: 19 }, { value: 18 }, + { value: 17 }, { value: 16 }, { value: 32 }, { value: 33 }, { value: 34 }, + { value: 35 }, 3, { value: 37 }, { value: 38 }, { value: 39 }, + { value: 40 }, { value: 41 }, { value: 42 }, { value: 43 }, { value: 44 }, + { value: 45 }, { value: 46 }, { value: 47 }, { value: 63 }, { value: 62 }, + { value: 61 }, { value: 60 }, { value: 59 }, { value: 58 }, { value: 57 }, + { value: 56 }, { value: 55 }, { value: 54 }, { value: 53 }, { value: 52 }, + { value: 51 }, { value: 50 }, { value: 49 }, +]; + +for (var i = 0; i < 1e6; ++i) { + array.includes(3); +} + diff --git a/JSTests/microbenchmarks/array-prototype-includes-int32.js b/JSTests/microbenchmarks/array-prototype-includes-int32.js new file mode 100644 index 0000000000000..14d020191a74c --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-int32.js @@ -0,0 +1,11 @@ +function test(array, searchElement) { + return array.includes(searchElement); +} +noInline(test); + +var array = new Array(1024); +for (var i = 0; i < array.length; i++) + array[i] = i; + +for (var i = 0; i < 1e6; ++i) + test(array, 512); diff --git a/JSTests/microbenchmarks/array-prototype-includes-string-16-const.js b/JSTests/microbenchmarks/array-prototype-includes-string-16-const.js new file mode 100644 index 0000000000000..43431b3909c40 --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-string-16-const.js @@ -0,0 +1,36 @@ +function test(array, value) { + return array.includes(value); +} +noInline(test); + +const array = [ + "あいうえおかきくけこさしすせそたちつてとなにぬねの", + "かきくけこさしすせそたちつてとなにぬねのはひふへほ", + "さしすせそたちつてとなにぬねのはひふへほまみむめも", + "たちつてとなにぬねのはひふへほまみむめもやゆよらり", + "なにぬねのはひふへほまみむめもやゆよらりるれろわを", + "はひふへほまみむめもやゆよらりるれろわをんあいうえ", + "まみむめもやゆよらりるれろわをんあいうえおかきくけ", + "やゆよらりるれろわをんあいうえおかきくけこさしすせ", + "らりるれろわをんあいうえおかきくけこさしすせそたち", + "わをんあいうえおかきくけこさしすせそたちつてとなに", + "んあいうえおかきくけこさしすせそたちつてとなにぬね", + "ひふへほまみむめもやゆよらりるれろわをんあいうえお", + "ふへほまみむめもやゆよらりるれろわをんあいうえおか", + "へほまみむめもやゆよらりるれろわをんあいうえおかき", + "ほまみむめもやゆよらりるれろわをんあいうえおかきく", + "みむめもやゆよらりるれろわをんあいうえおかきくけこ", + "むめもやゆよらりるれろわをんあいうえおかきくけこさ", + "めもやゆよらりるれろわをんあいうえおかきくけこさし", + "もやゆよらりるれろわをんあいうえおかきくけこさしす", + "ゆよらりるれろわをんあいうえおかきくけこさしすせそ", + "よらりるれろわをんあいうえおかきくけこさしすせそた", + "らりるれろわをんあいうえおかきくけこさしすせそたち", + "りるれろわをんあいうえおかきくけこさしすせそたちつ", + "るれろわをんあいうえおかきくけこさしすせそたちつて", + "れろわをんあいうえおかきくけこさしすせそたちつてと", + "ろわをんあいうえおかきくけこさしすせそたちつてとな", +]; + +for (var i = 0; i < 1e6; ++i) + test(array, "もやゆよらりるれろわをんあいうえおかきくけこさしす"); diff --git a/JSTests/microbenchmarks/array-prototype-includes-string-16.js b/JSTests/microbenchmarks/array-prototype-includes-string-16.js new file mode 100644 index 0000000000000..39bbf59ea7174 --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-string-16.js @@ -0,0 +1,36 @@ +function test(array, value) { + return array.includes(value); +} +noInline(test); + +const array = [ + "あいうえおかきくけこさしすせそたちつてとなにぬねの", + "かきくけこさしすせそたちつてとなにぬねのはひふへほ", + "さしすせそたちつてとなにぬねのはひふへほまみむめも", + "たちつてとなにぬねのはひふへほまみむめもやゆよらり", + "なにぬねのはひふへほまみむめもやゆよらりるれろわを", + "はひふへほまみむめもやゆよらりるれろわをんあいうえ", + "まみむめもやゆよらりるれろわをんあいうえおかきくけ", + "やゆよらりるれろわをんあいうえおかきくけこさしすせ", + "らりるれろわをんあいうえおかきくけこさしすせそたち", + "わをんあいうえおかきくけこさしすせそたちつてとなに", + "んあいうえおかきくけこさしすせそたちつてとなにぬね", + "ひふへほまみむめもやゆよらりるれろわをんあいうえお", + "ふへほまみむめもやゆよらりるれろわをんあいうえおか", + "へほまみむめもやゆよらりるれろわをんあいうえおかき", + "ほまみむめもやゆよらりるれろわをんあいうえおかきく", + "みむめもやゆよらりるれろわをんあいうえおかきくけこ", + "むめもやゆよらりるれろわをんあいうえおかきくけこさ", + "めもやゆよらりるれろわをんあいうえおかきくけこさし", + "もやゆよらりるれろわをんあいうえおかきくけこさしす", + "ゆよらりるれろわをんあいうえおかきくけこさしすせそ", + "よらりるれろわをんあいうえおかきくけこさしすせそた", + "らりるれろわをんあいうえおかきくけこさしすせそたち", + "りるれろわをんあいうえおかきくけこさしすせそたちつ", + "るれろわをんあいうえおかきくけこさしすせそたちつて", + "れろわをんあいうえおかきくけこさしすせそたちつてと", + "ろわをんあいうえおかきくけこさしすせそたちつてとな", +]; + +for (var i = 0; i < 1e6; ++i) + test(array, "もやゆよらりるれろわをん" + "あいうえおかきくけこさしす"); diff --git a/JSTests/microbenchmarks/array-prototype-includes-string-const.js b/JSTests/microbenchmarks/array-prototype-includes-string-const.js new file mode 100644 index 0000000000000..3baf296813689 --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-string-const.js @@ -0,0 +1,35 @@ +function test(array, index) { + return array.includes(index); +} +noInline(test); + +const array = [ + "dEXt0TxZQQQasd999!@#$%d^&", + "AAZZ!!@@**CC77zzxx1122d33", + "HelloWorldHeldloWorlddABC", + "abcABC123!@#xfyzXYZ!$d%+=", + "Zyx9!Zyx9!Zyx9!Zyx9!!d???", + "LoremIpsum1234!@#$LordemI", + "QQQQQQQQQQqqqqqqqqqq-----", + "&&&&1111%%%%2222@@@@33f33", + "TestStringasdVariousChars", + "RandomASCII~d~~~====?????", + "^^^^#####^^^f^^+++++.....", + "AZaz09!?AZaz09!?AZaz09!?%", + "Foobar##1122Foobar##1122F", + "9876543210!@d#$%^&*()_+=-", + "OneTwo3FourFive6Seven8Nin", + "Zwxy000111Zwxy000111Zwxy0", + "Spark!!!Spark???Spark%%%S", + "ShortAndSweet123??!!Short", + "BenchmarkCaseHere12345!!?", + "MultiLineString###000xxx@", + "ABCDEFGHabcdfefgh1234!!!!", + "111122223333d4444!!!!####", + "EndlessVariety?!@#$%^^^^^", + "PqrstUVWX9876pqrstUVWX987", + "MixItUpWith5omeWe!rdCHARS", + "FinallyZzYyXx!@#$4321)(*&", +]; +for (var i = 0; i < 1e6; ++i) + test(array, "BenchmarkCaseHere12345!!?"); diff --git a/JSTests/microbenchmarks/array-prototype-includes-string.js b/JSTests/microbenchmarks/array-prototype-includes-string.js new file mode 100644 index 0000000000000..d42f1c8498625 --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-string.js @@ -0,0 +1,35 @@ +function test(array, index) { + return array.includes(index); +} +noInline(test); + +const array = [ + "dEXt0TxZQQQasd999!@#$%d^&", + "AAZZ!!@@**CC77zzxx1122d33", + "HelloWorldHeldloWorlddABC", + "abcABC123!@#xfyzXYZ!$d%+=", + "Zyx9!Zyx9!Zyx9!Zyx9!!d???", + "LoremIpsum1234!@#$LordemI", + "QQQQQQQQQQqqqqqqqqqq-----", + "&&&&1111%%%%2222@@@@33f33", + "TestStringasdVariousChars", + "RandomASCII~d~~~====?????", + "^^^^#####^^^f^^+++++.....", + "AZaz09!?AZaz09!?AZaz09!?%", + "Foobar##1122Foobar##1122F", + "9876543210!@d#$%^&*()_+=-", + "OneTwo3FourFive6Seven8Nin", + "Zwxy000111Zwxy000111Zwxy0", + "Spark!!!Spark???Spark%%%S", + "ShortAndSweet123??!!Short", + "BenchmarkCaseHere12345!!?", + "MultiLineString###000xxx@", + "ABCDEFGHabcdfefgh1234!!!!", + "111122223333d4444!!!!####", + "EndlessVariety?!@#$%^^^^^", + "PqrstUVWX9876pqrstUVWX987", + "MixItUpWith5omeWe!rdCHARS", + "FinallyZzYyXx!@#$4321)(*&", +]; +for (var i = 0; i < 1e6; ++i) + test(array, "Benchmark" + "CaseHere12345!!?"); diff --git a/JSTests/stress/array-prototype-includes-contiguous-nan.js b/JSTests/stress/array-prototype-includes-contiguous-nan.js new file mode 100644 index 0000000000000..c481f4e0a1556 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-contiguous-nan.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [{}, {}, NaN, {}, {}]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(NaN), true); +} diff --git a/JSTests/stress/array-prototype-includes-double-nan.js b/JSTests/stress/array-prototype-includes-double-nan.js new file mode 100644 index 0000000000000..251e236b4f5eb --- /dev/null +++ b/JSTests/stress/array-prototype-includes-double-nan.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [1.2, 2.4, NaN, 4.4, 5.4]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(NaN), true); +} diff --git a/JSTests/stress/array-prototype-includes-doublerepuse.js b/JSTests/stress/array-prototype-includes-doublerepuse.js new file mode 100644 index 0000000000000..6a8f29f98f6c4 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-doublerepuse.js @@ -0,0 +1,13 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([1, 2, 3.4, 4, 5, 6, 6, 4], 3.4, true); +test([1, 2, 3.4, 4, 5, 6, 6, 4], 4.5, false); diff --git a/JSTests/stress/array-prototype-includes-hole-contiguous.js b/JSTests/stress/array-prototype-includes-hole-contiguous.js new file mode 100644 index 0000000000000..33a45fbe902b6 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-hole-contiguous.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [{}, {}, {}, , {}]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(undefined), true); +} diff --git a/JSTests/stress/array-prototype-includes-hole-double.js b/JSTests/stress/array-prototype-includes-hole-double.js new file mode 100644 index 0000000000000..1fc5275e26f7c --- /dev/null +++ b/JSTests/stress/array-prototype-includes-hole-double.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [1.4, 2.4, , 4.4, 5.5]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(undefined), true); +} diff --git a/JSTests/stress/array-prototype-includes-hole-int32.js b/JSTests/stress/array-prototype-includes-hole-int32.js new file mode 100644 index 0000000000000..eaed2968a53d8 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-hole-int32.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [1, 2, , 4, 5]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(undefined), true); +} diff --git a/JSTests/stress/array-prototype-includes-int32-nan.js b/JSTests/stress/array-prototype-includes-int32-nan.js new file mode 100644 index 0000000000000..a77b8cf24a239 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-int32-nan.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [1, 2, NaN, 4, 5]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(NaN), true); +} diff --git a/JSTests/stress/array-prototype-includes-int32use.js b/JSTests/stress/array-prototype-includes-int32use.js new file mode 100644 index 0000000000000..fe536ed899e01 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-int32use.js @@ -0,0 +1,13 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([1, 2, 3, 4, 5, 6, 6, 4], 5, true); +test([1, 2, 3, 4, 5, 6, 6, 4], 32, false); diff --git a/JSTests/stress/array-prototype-includes-objectuse.js b/JSTests/stress/array-prototype-includes-objectuse.js new file mode 100644 index 0000000000000..5a9ce8dfdf2d5 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-objectuse.js @@ -0,0 +1,14 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +const obj = {}; +test([1, 2, 3.4, obj, 5, 6, 6, 4], obj, true); +test([1, 2, 3.4, {}, 5, 6, 6, 4], obj, false); diff --git a/JSTests/stress/array-prototype-includes-otheruse.js b/JSTests/stress/array-prototype-includes-otheruse.js new file mode 100644 index 0000000000000..4c99a83f8fe33 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-otheruse.js @@ -0,0 +1,16 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([1, 2, 3.4, null, 5, 6, 6, 4], null, true); +test([1, 2, 3.4, undefined, 5, 6, 6, 4], undefined, true); +test([1, 2, 3.4, null, 5, 6, 6, 4], undefined, false); +test([1, 2, 3.4, undefined, 5, 6, 6, 4], null, false); + diff --git a/JSTests/stress/array-prototype-includes-stringuse.js b/JSTests/stress/array-prototype-includes-stringuse.js new file mode 100644 index 0000000000000..2912602b824c0 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-stringuse.js @@ -0,0 +1,13 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([1, 2, 3.4, "foo", 5, 6, 6, 4], "foo", true); +test([1, 2, 3.4, "foo", 5, 6, 6, 4], "bar", false); diff --git a/JSTests/stress/array-prototype-includes-symboluse.js b/JSTests/stress/array-prototype-includes-symboluse.js new file mode 100644 index 0000000000000..49745684066c3 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-symboluse.js @@ -0,0 +1,14 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +const sym = Symbol("foo"); +test([1, 2, 3.4, sym, 5, 6, 6, 4], sym, true); +test([1, 2, 3.4, Symbol("bar"), 5, 6, 6, 4], sym, false); diff --git a/JSTests/stress/array-prototype-includes-untypeduse-contiguous.js b/JSTests/stress/array-prototype-includes-untypeduse-contiguous.js new file mode 100644 index 0000000000000..4ac96a1c88055 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-untypeduse-contiguous.js @@ -0,0 +1,13 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([{}, 3.4, 3.5, 4.5, 5.4, 6.2, 6.2, 4.5], 4.5, true); +test([{}, 3.4, 3.5, 4.5, 5.4, 6.2, 6.2, 4.5], 5.9, false); diff --git a/JSTests/stress/array-prototype-includes-untypeduse-int32.js b/JSTests/stress/array-prototype-includes-untypeduse-int32.js new file mode 100644 index 0000000000000..85f41faddd2d3 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-untypeduse-int32.js @@ -0,0 +1,13 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([1, 2, 3, 4, 5, 6, 6, 4], 3.4, false); +test([1, 2, 3, 4, 5, 6, 6, 4], 5, true); diff --git a/Source/JavaScriptCore/builtins/ArrayPrototype.js b/Source/JavaScriptCore/builtins/ArrayPrototype.js index f7e77db9acf42..7618310f7b6aa 100644 --- a/Source/JavaScriptCore/builtins/ArrayPrototype.js +++ b/Source/JavaScriptCore/builtins/ArrayPrototype.js @@ -273,40 +273,6 @@ function findLastIndex(callback /*, thisArg */) return -1; } -function includes(searchElement /*, fromIndex*/) -{ - "use strict"; - - var array = @toObject(this, "Array.prototype.includes requires that |this| not be null or undefined"); - var length = @toLength(array.length); - - if (length === 0) - return false; - - var fromIndex = 0; - var from = @argument(1); - if (from !== @undefined) - fromIndex = @toIntegerOrInfinity(from); - - var index; - if (fromIndex >= 0) - index = fromIndex; - else - index = length + fromIndex; - - if (index < 0) - index = 0; - - var currentElement; - for (; index < length; ++index) { - currentElement = array[index]; - // Use SameValueZero comparison, rather than just StrictEquals. - if (searchElement === currentElement || (searchElement !== searchElement && currentElement !== currentElement)) - return true; - } - return false; -} - @linkTimeConstant function maxWithPositives(a, b) { diff --git a/Source/JavaScriptCore/builtins/BuiltinNames.h b/Source/JavaScriptCore/builtins/BuiltinNames.h index 3ecf7c0a084cc..ee15b67193d10 100644 --- a/Source/JavaScriptCore/builtins/BuiltinNames.h +++ b/Source/JavaScriptCore/builtins/BuiltinNames.h @@ -219,6 +219,7 @@ namespace JSC { macro(regExpStringIteratorCreate) \ macro(iteratorHelperCreate) \ macro(syncIterator) \ + macro(includes) \ namespace Symbols { diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h index 4834aa0ff6a59..5391fbba0aaef 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h @@ -2946,6 +2946,10 @@ bool AbstractInterpreter::executeEffects(unsigned clobberLimi makeBytecodeTopForNode(node); break; + case ArrayIncludes: + setNonCellTypeForNode(node, SpecBoolean); + break; + case ArrayIndexOf: { setNonCellTypeForNode(node, SpecInt32Only); break; diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index 866df01f8490a..25df3adb82f83 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -2749,6 +2749,7 @@ auto ByteCodeParser::handleIntrinsicCall(Node* callee, Operand resultOperand, Ca return CallOptimizationResult::Inlined; } + case ArrayIncludesIntrinsic: case ArrayIndexOfIntrinsic: { if (argumentCountIncludingThis < 2) return CallOptimizationResult::DidNothing; @@ -2792,7 +2793,9 @@ auto ByteCodeParser::handleIntrinsicCall(Node* callee, Operand resultOperand, Ca addVarArgChild(get(virtualRegisterForArgumentIncludingThis(2, registerOffset))); // Start index. addVarArgChild(nullptr); - Node* node = addToGraph(Node::VarArg, ArrayIndexOf, OpInfo(arrayMode.asWord()), OpInfo()); + Node* node = intrinsic == ArrayIncludesIntrinsic + ? addToGraph(Node::VarArg, ArrayIncludes, OpInfo(arrayMode.asWord()), OpInfo()) + : addToGraph(Node::VarArg, ArrayIndexOf, OpInfo(arrayMode.asWord()), OpInfo()); setResult(node); return CallOptimizationResult::Inlined; } diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h index f81b0c909a7d8..7852ae140bd93 100644 --- a/Source/JavaScriptCore/dfg/DFGClobberize.h +++ b/Source/JavaScriptCore/dfg/DFGClobberize.h @@ -167,6 +167,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu case ArrayifyToStructure: case ArrayPush: case ArrayPop: + case ArrayIncludes: case ArrayIndexOf: case HasIndexedProperty: case AtomicsAdd: @@ -684,6 +685,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu write(HeapObjectCount); return; + case ArrayIncludes: case ArrayIndexOf: { // FIXME: Should support a CSE rule. // https://bugs.webkit.org/show_bug.cgi?id=173173 diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp index 2fe7e4b0a0435..1f755e8f41736 100644 --- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp +++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp @@ -447,6 +447,7 @@ bool doesGC(Graph& graph, Node* node) case CallDOMGetter: case CallDOM: case ArraySlice: + case ArrayIncludes: case ArrayIndexOf: case ParseInt: // We might resolve a rope even though we don't clobber anything. case SetAdd: diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 8f6b79f00c7fa..4243e859e7fdd 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -1623,8 +1623,9 @@ class FixupPhase : public Phase { break; } + case ArrayIncludes: case ArrayIndexOf: - fixupArrayIndexOf(node); + fixupArrayIndexOfOrArrayIncludes(node); break; case RegExpExec: @@ -4791,12 +4792,19 @@ class FixupPhase : public Phase { fixup(node->child3(), 1); } - void fixupArrayIndexOf(Node* node) + void fixupArrayIndexOfOrArrayIncludes(Node* node) { + ASSERT(node->op() == ArrayIndexOf || node->op() == ArrayIncludes); + + bool isArrayIncludes = node->op() == ArrayIncludes; + Edge& array = m_graph.varArgChild(node, 0); Edge& storage = m_graph.varArgChild(node, node->numChildren() == 3 ? 2 : 3); blessArrayOperation(array, Edge(), storage); - ASSERT_WITH_MESSAGE(storage.node(), "blessArrayOperation for ArrayIndexOf must set Butterfly for storage edge."); + if (isArrayIncludes) + ASSERT_WITH_MESSAGE(storage.node(), "blessArrayOperation for ArrayIncludes must set Butterfly for storage edge."); + else + ASSERT_WITH_MESSAGE(storage.node(), "blessArrayOperation for ArrayIndexOf must set Butterfly for storage edge."); Edge& searchElement = m_graph.varArgChild(node, 1); @@ -4813,15 +4821,21 @@ class FixupPhase : public Phase { if (searchElement->shouldSpeculateCell()) { fixEdge(searchElement); m_insertionSet.insertCheck(m_graph, m_indexInBlock, node); - m_graph.convertToConstant(node, jsNumber(-1)); + if (isArrayIncludes) + m_graph.convertToConstant(node, jsBoolean(false)); + else + m_graph.convertToConstant(node, jsNumber(-1)); observeUseKindOnNode(searchElementNode); return; } - if (searchElement->shouldSpeculateOther()) { + if (searchElement->shouldSpeculateOther() && !isArrayIncludes) { fixEdge(searchElement); m_insertionSet.insertCheck(m_graph, m_indexInBlock, node); - m_graph.convertToConstant(node, jsNumber(-1)); + if (isArrayIncludes) + m_graph.convertToConstant(node, jsBoolean(false)); + else + m_graph.convertToConstant(node, jsNumber(-1)); observeUseKindOnNode(searchElementNode); return; } @@ -4829,7 +4843,10 @@ class FixupPhase : public Phase { if (searchElement->shouldSpeculateBoolean()) { fixEdge(searchElement); m_insertionSet.insertCheck(m_graph, m_indexInBlock, node); - m_graph.convertToConstant(node, jsNumber(-1)); + if (isArrayIncludes) + m_graph.convertToConstant(node, jsBoolean(false)); + else + m_graph.convertToConstant(node, jsNumber(-1)); observeUseKindOnNode(searchElementNode); return; } diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index ff15c21074c12..c72320bcf619f 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -2231,6 +2231,7 @@ struct Node { case GetTypedArrayLengthAsInt52: case HasIndexedProperty: case EnumeratorNextUpdateIndexAndMode: + case ArrayIncludes: case ArrayIndexOf: return true; default: @@ -2576,6 +2577,7 @@ struct Node { case ArrayifyToStructure: case ArrayPush: case ArrayPop: + case ArrayIncludes: case ArrayIndexOf: case HasIndexedProperty: case AtomicsAdd: diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index 3bbc995c7354f..73b7f7bf4b188 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -334,6 +334,7 @@ namespace JSC { namespace DFG { macro(ArrayPush, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \ macro(ArrayPop, NodeResultJS | NodeMustGenerate) \ macro(ArraySlice, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \ + macro(ArrayIncludes, NodeResultBoolean | NodeHasVarArgs) \ macro(ArrayIndexOf, NodeResultInt32 | NodeHasVarArgs) \ macro(ArraySplice, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \ \ diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 2c4caa5a9b9e2..5b4083987b273 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -3706,6 +3706,131 @@ JSC_DEFINE_JIT_OPERATION(operationToLengthUntyped, EncodedJSValue, (JSGlobalObje OPERATION_RETURN(scope, JSValue::encode(jsNumber(value.toLength(globalObject)))); } +static ALWAYS_INLINE UCPUStrictInt32 arrayIncludesString(JSGlobalObject* globalObject, Butterfly* butterfly, JSString* searchElement, int32_t index) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t length = butterfly->publicLength(); + auto data = butterfly->contiguous().data(); + for (; index < length; ++index) { + JSValue value = data[index].get(); + if (!value || !value.isString()) + continue; + auto* string = asString(value); + if (string == searchElement) + return toUCPUStrictInt32(1); + if (string->equalInline(globalObject, searchElement)) { + scope.assertNoExceptionExceptTermination(); + return toUCPUStrictInt32(1); + } + RETURN_IF_EXCEPTION(scope, { }); + } + return toUCPUStrictInt32(0); +} + +JSC_DEFINE_JIT_OPERATION(operationArrayIncludesString, UCPUStrictInt32, (JSGlobalObject* globalObject, Butterfly* butterfly, JSString* searchElement, int32_t index)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + OPERATION_RETURN(scope, arrayIncludesString(globalObject, butterfly, searchElement, index)); +} + +JSC_DEFINE_JIT_OPERATION(operationArrayIncludesValueInt32OrContiguous, UCPUStrictInt32, (JSGlobalObject* globalObject, Butterfly* butterfly, EncodedJSValue encodedValue, int32_t index)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue searchElement = JSValue::decode(encodedValue); + + if (searchElement.isString()) + OPERATION_RETURN(scope, arrayIncludesString(globalObject, butterfly, asString(searchElement), index)); + + int32_t length = butterfly->publicLength(); + auto data = butterfly->contiguous().data(); + + if (index >= length) + OPERATION_RETURN(scope, toUCPUStrictInt32(0)); + + if (searchElement.isObject()) { + auto* result = std::bit_cast*>(WTF::find64(std::bit_cast(data + index), encodedValue, length - index)); + if (result) + OPERATION_RETURN(scope, toUCPUStrictInt32(1)); + OPERATION_RETURN(scope, toUCPUStrictInt32(0)); + } + + if (searchElement.isInt32()) { + for (; index < length; ++index) { + JSValue value = data[index].get(); + if (!value || !value.isInt32()) + continue; + if (searchElement.asInt32() == value.asInt32()) + OPERATION_RETURN(scope, toUCPUStrictInt32(1)); + } + OPERATION_RETURN(scope, toUCPUStrictInt32(0)); + } + + bool searchElementIsUndefined = searchElement.isUndefined(); + for (; index < length; ++index) { + JSValue value = data[index].get(); + if (!value) { + if (searchElementIsUndefined) + OPERATION_RETURN(scope, 1); + continue; + } + bool isEqual = sameValueZero(globalObject, searchElement, value); + OPERATION_RETURN_IF_EXCEPTION(scope, 0); + if (isEqual) + OPERATION_RETURN(scope, toUCPUStrictInt32(1)); + } + OPERATION_RETURN(scope, toUCPUStrictInt32(0)); +} + +JSC_DEFINE_NOEXCEPT_JIT_OPERATION(operationArrayIncludesValueDouble, UCPUStrictInt32, (Butterfly* butterfly, EncodedJSValue encodedValue, int32_t index)) +{ + // We do not cause any exceptions, thus we do not need FrameTracers. + JSValue searchElement = JSValue::decode(encodedValue); + const double* data = butterfly->contiguousDouble().data(); + int32_t length = butterfly->publicLength(); + + if (searchElement.isUndefined() && containsHole(data, length)) + return toUCPUStrictInt32(1); + if (!searchElement.isNumber()) + return toUCPUStrictInt32(0); + + double number = searchElement.asNumber(); + for (; index < length; ++index) { + // This comparison ignores NaN. + if (data[index] == number) + return toUCPUStrictInt32(1); + } + return toUCPUStrictInt32(0); +} + +JSC_DEFINE_NOEXCEPT_JIT_OPERATION(operationArrayIncludesNonStringIdentityValueContiguous, UCPUStrictInt32, (Butterfly* butterfly, EncodedJSValue searchElement, int32_t index)) +{ + // We do not cause any exceptions, thus we do not need FrameTracers. + int32_t length = butterfly->publicLength(); + auto data = butterfly->contiguous().data(); + + if (index >= length) + return toUCPUStrictInt32(0); + + auto* result = std::bit_cast*>(WTF::find64(std::bit_cast(data + index), searchElement, length - index)); + if (result) + return toUCPUStrictInt32(1); + + JSValue searchElementValue = JSValue::decode(searchElement); + if (searchElementValue.isUndefined() && containsHole(data, length)) + return toUCPUStrictInt32(1); + return toUCPUStrictInt32(0); +} + static ALWAYS_INLINE UCPUStrictInt32 arrayIndexOfString(JSGlobalObject* globalObject, Butterfly* butterfly, JSString* searchElement, int32_t index) { VM& vm = globalObject->vm(); diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h index 882ed29be1a19..db15d0d5687c6 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.h +++ b/Source/JavaScriptCore/dfg/DFGOperations.h @@ -368,6 +368,11 @@ JSC_DECLARE_JIT_OPERATION(operationThrowStaticError, void, (JSGlobalObject*, JSS JSC_DECLARE_JIT_OPERATION(operationHasOwnProperty, size_t, (JSGlobalObject*, JSObject*, EncodedJSValue)); +JSC_DECLARE_JIT_OPERATION(operationArrayIncludesString, UCPUStrictInt32, (JSGlobalObject*, Butterfly*, JSString*, int32_t)); +JSC_DECLARE_NOEXCEPT_JIT_OPERATION(operationArrayIncludesValueDouble, UCPUStrictInt32, (Butterfly*, EncodedJSValue, int32_t)); +JSC_DECLARE_JIT_OPERATION(operationArrayIncludesValueInt32OrContiguous, UCPUStrictInt32, (JSGlobalObject*, Butterfly*, EncodedJSValue, int32_t)); +JSC_DECLARE_NOEXCEPT_JIT_OPERATION(operationArrayIncludesNonStringIdentityValueContiguous, UCPUStrictInt32, (Butterfly*, EncodedJSValue, int32_t)); + JSC_DECLARE_JIT_OPERATION(operationArrayIndexOfString, UCPUStrictInt32, (JSGlobalObject*, Butterfly*, JSString*, int32_t)); JSC_DECLARE_NOEXCEPT_JIT_OPERATION(operationArrayIndexOfValueDouble, UCPUStrictInt32, (Butterfly*, EncodedJSValue, int32_t)); JSC_DECLARE_JIT_OPERATION(operationArrayIndexOfValueInt32OrContiguous, UCPUStrictInt32, (JSGlobalObject*, Butterfly*, EncodedJSValue, int32_t)); diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index af6511d1110b1..7a5390dbc6eb9 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -1144,6 +1144,11 @@ class PredictionPropagationPhase : public Phase { break; } + case ArrayIncludes: { + setPrediction(SpecBoolean); + break; + } + case GetTypedArrayByteOffset: case GetArrayLength: case GetUndetachedTypeArrayLength: diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h index c75f3587be170..069c4948ce7b8 100644 --- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h +++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h @@ -350,6 +350,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno return state.forNode(node->child1()).isType(SpecObject); case ArraySlice: + case ArrayIncludes: case ArrayIndexOf: { // You could plausibly move this code around as long as you proved the // incoming array base structure is an original array at the hoisted location. diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index c2a7be72d1ad2..6524ac1962c73 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -9476,9 +9476,11 @@ void SpeculativeJIT::compileArraySplice(Node* node) jsValueResult(resultRegs, node); } -void SpeculativeJIT::compileArrayIndexOf(Node* node) +void SpeculativeJIT::compileArrayIndexOfOrArrayIncludes(Node* node) { - ASSERT(node->op() == ArrayIndexOf); + ASSERT(node->op() == ArrayIndexOf || node->op() == ArrayIncludes); + + bool isArrayIncludes = node->op() == ArrayIncludes; StorageOperand storage(this, m_graph.varArgChild(node, node->numChildren() == 3 ? 2 : 3)); GPRTemporary index(this); @@ -9514,10 +9516,20 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) add32(TrustedImm32(1), indexGPR); jump().linkTo(loop, this); - notFound.link(this); - move(TrustedImm32(-1), indexGPR); - found.link(this); - strictInt32Result(indexGPR, node); + if (isArrayIncludes) { + notFound.link(this); + move(TrustedImm32(0), indexGPR); + Jump done = jump(); + found.link(this); + move(TrustedImm32(1), indexGPR); + done.link(this); + unblessedBooleanResult(indexGPR, node); + } else { + notFound.link(this); + move(TrustedImm32(-1), indexGPR); + found.link(this); + strictInt32Result(indexGPR, node); + } }; ASSERT(node->arrayMode().type() == Array::Int32); @@ -9569,10 +9581,20 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) add32(TrustedImm32(1), indexGPR); jump().linkTo(loop, this); - notFound.link(this); - move(TrustedImm32(-1), indexGPR); - found.link(this); - strictInt32Result(indexGPR, node); + if (isArrayIncludes) { + notFound.link(this); + move(TrustedImm32(0), indexGPR); + Jump done = jump(); + found.link(this); + move(TrustedImm32(1), indexGPR); + done.link(this); + unblessedBooleanResult(indexGPR, node); + } else { + notFound.link(this); + move(TrustedImm32(-1), indexGPR); + found.link(this); + strictInt32Result(indexGPR, node); + } return; } @@ -9587,9 +9609,13 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) flushRegisters(); - callOperation(operationArrayIndexOfString, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementGPR, indexGPR); - - strictInt32Result(lengthGPR, node); + if (isArrayIncludes) { + callOperation(operationArrayIncludesString, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementGPR, indexGPR); + unblessedBooleanResult(lengthGPR, node); + } else { + callOperation(operationArrayIndexOfString, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementGPR, indexGPR); + strictInt32Result(lengthGPR, node); + } return; #else @@ -9634,10 +9660,18 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) add32(TrustedImm32(1), indexGPR); jump().linkTo(loop, this); - notFound.link(this); - move(TrustedImm32(-1), indexGPR); - - found.link(this); + if (isArrayIncludes) { + notFound.link(this); + move(TrustedImm32(0), indexGPR); + Jump done = jump(); + found.link(this); + move(TrustedImm32(1), indexGPR); + done.link(this); + } else { + notFound.link(this); + move(TrustedImm32(-1), indexGPR); + found.link(this); + } }; auto emitCompare = [&]() -> JumpList { @@ -9692,13 +9726,21 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) emitLoop(emitCompare); - addSlowPathGenerator(slowPathCall( - slowCase, this, operationArrayIndexOfString, - indexGPR, LinkableConstant::globalObject(*this, node), - storageGPR, searchElementGPR, indexGPR - )); - - strictInt32Result(indexGPR, node); + if (isArrayIncludes) { + addSlowPathGenerator(slowPathCall( + slowCase, this, operationArrayIncludesString, + indexGPR, LinkableConstant::globalObject(*this, node), + storageGPR, searchElementGPR, indexGPR + )); + unblessedBooleanResult(indexGPR, node); + } else { + addSlowPathGenerator(slowPathCall( + slowCase, this, operationArrayIndexOfString, + indexGPR, LinkableConstant::globalObject(*this, node), + storageGPR, searchElementGPR, indexGPR + )); + strictInt32Result(indexGPR, node); + } return; #endif @@ -9715,8 +9757,13 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) ASSERT(node->arrayMode().type() == Array::Contiguous); flushRegisters(); - callOperationWithoutExceptionCheck(operationArrayIndexOfNonStringIdentityValueContiguous, lengthGPR, storageGPR, valueRegs, indexGPR); - strictInt32Result(lengthGPR, node); + if (isArrayIncludes) { + callOperationWithoutExceptionCheck(operationArrayIncludesNonStringIdentityValueContiguous, lengthGPR, storageGPR, valueRegs, indexGPR); + unblessedBooleanResult(lengthGPR, node); + } else { + callOperationWithoutExceptionCheck(operationArrayIndexOfNonStringIdentityValueContiguous, lengthGPR, storageGPR, valueRegs, indexGPR); + strictInt32Result(lengthGPR, node); + } return; } @@ -9728,18 +9775,27 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) flushRegisters(); switch (node->arrayMode().type()) { case Array::Double: - callOperation(operationArrayIndexOfValueDouble, lengthGPR, storageGPR, searchElementRegs, indexGPR); + if (isArrayIncludes) + callOperation(operationArrayIncludesValueDouble, lengthGPR, storageGPR, searchElementRegs, indexGPR); + else + callOperation(operationArrayIndexOfValueDouble, lengthGPR, storageGPR, searchElementRegs, indexGPR); break; case Array::Int32: case Array::Contiguous: - callOperation(operationArrayIndexOfValueInt32OrContiguous, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementRegs, indexGPR); + if (isArrayIncludes) + callOperation(operationArrayIncludesValueInt32OrContiguous, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementRegs, indexGPR); + else + callOperation(operationArrayIndexOfValueInt32OrContiguous, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementRegs, indexGPR); break; default: RELEASE_ASSERT_NOT_REACHED(); break; } - strictInt32Result(lengthGPR, node); + if (isArrayIncludes) + unblessedBooleanResult(lengthGPR, node); + else + strictInt32Result(lengthGPR, node); return; } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 62b5187681cd0..084212562e026 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -1686,7 +1686,7 @@ class SpeculativeJIT : public JITCompiler { void compileGetRestLength(Node*); void compileArraySlice(Node*); void compileArraySplice(Node*); - void compileArrayIndexOf(Node*); + void compileArrayIndexOfOrArrayIncludes(Node*); void compileArrayPush(Node*); void compileNotifyWrite(Node*); void compileRegExpExec(Node*); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index e74f25b5cf3f8..d807d6c91cd8d 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -2932,8 +2932,9 @@ void SpeculativeJIT::compile(Node* node) break; } + case ArrayIncludes: case ArrayIndexOf: { - compileArrayIndexOf(node); + compileArrayIndexOfOrArrayIncludes(node); break; } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index a3c7bc88a3980..cab6e82492629 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -4154,8 +4154,9 @@ void SpeculativeJIT::compile(Node* node) break; } + case ArrayIncludes: case ArrayIndexOf: { - compileArrayIndexOf(node); + compileArrayIndexOfOrArrayIncludes(node); break; } diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp index 94b1463827e89..ffdeaf2a714fc 100644 --- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp +++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp @@ -422,6 +422,7 @@ inline CapabilityLevel canCompile(Node* node) case CallDOMGetter: case ArraySlice: case ArraySplice: + case ArrayIncludes: case ArrayIndexOf: case ArrayPop: case ArrayPush: diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp index 830c23c3de385..9193e3347fb6b 100644 --- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp +++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp @@ -1160,8 +1160,9 @@ class LowerDFGToB3 { case ArraySplice: compileArraySplice(); break; + case ArrayIncludes: case ArrayIndexOf: - compileArrayIndexOf(); + compileArrayIndexOfOrArrayIncludes(); break; case CreateActivation: compileCreateActivation(); @@ -7592,8 +7593,10 @@ IGNORE_CLANG_WARNINGS_END setJSValue(vmCall(Int64, refCount ? operationArraySplice : operationArraySpliceIgnoreResult, weakPointer(globalObject), base, index, deleteCount, m_out.constIntPtr(buffer), m_out.constInt32(insertionCount))); } - void compileArrayIndexOf() + void compileArrayIndexOfOrArrayIncludes() { + ASSERT(m_node->op() == ArrayIncludes || m_node->op() == ArrayIndexOf); + JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic); LValue base = lowCell(m_graph.varArgChild(m_node, 0)); LValue storage = lowStorage(m_node->numChildren() == 3 ? m_graph.varArgChild(m_node, 2) : m_graph.varArgChild(m_node, 3)); @@ -7608,6 +7611,8 @@ IGNORE_CLANG_WARNINGS_END } else startIndex = m_out.int32Zero; + bool isArrayIncludes = m_node->op() == ArrayIncludes; + Edge& searchElementEdge = m_graph.varArgChild(m_node, 1); switch (searchElementEdge.useKind()) { case Int32Use: @@ -7645,7 +7650,7 @@ IGNORE_CLANG_WARNINGS_END m_out.branch(m_out.notEqual(index, length), unsure(loopBody), unsure(notFound)); m_out.appendTo(loopBody, loopNext); - ValueFromBlock foundResult = m_out.anchor(index); + ValueFromBlock foundResult = isArrayIncludes ? m_out.anchor(m_out.constBool(true)) : m_out.anchor(index); switch (searchElementEdge.useKind()) { case Int32Use: { // Empty value is ignored because of JSValue::NumberTag. @@ -7670,13 +7675,16 @@ IGNORE_CLANG_WARNINGS_END m_out.jump(loopHeader); m_out.appendTo(notFound, continuation); - ValueFromBlock notFoundResult = m_out.anchor(m_out.constIntPtr(-1)); + ValueFromBlock notFoundResult = isArrayIncludes ? m_out.anchor(m_out.constBool(false)) : m_out.anchor(m_out.constIntPtr(-1)); m_out.jump(continuation); m_out.appendTo(continuation, lastNext); // We have to keep base alive since that keeps content of storage alive. ensureStillAliveHere(base); - setInt32(m_out.castToInt32(m_out.phi(pointerType(), notFoundResult, foundResult))); + if (isArrayIncludes) + setBoolean(m_out.phi(Int32, notFoundResult, foundResult)); + else + setInt32(m_out.castToInt32(m_out.phi(pointerType(), notFoundResult, foundResult))); return; } @@ -7726,7 +7734,7 @@ IGNORE_CLANG_WARNINGS_END m_out.branch(isString(element), unsure(fastPath), unsure(loopNext)); m_out.appendTo(fastPath, slowCheckElementRope); - ValueFromBlock foundResult = m_out.anchor(index); + ValueFromBlock foundResult = isArrayIncludes ? m_out.anchor(m_out.constBool(true)) : m_out.anchor(index); m_out.branch(m_out.equal(element, searchElement), unsure(continuation), unsure(slowCheckElementRope)); m_out.appendTo(slowCheckElementRope, slowCheckElement8Bit); @@ -7775,17 +7783,20 @@ IGNORE_CLANG_WARNINGS_END m_out.jump(loopHeader); m_out.appendTo(notFound, continuation); - ValueFromBlock notFoundResult = m_out.anchor(m_out.constIntPtr(-1)); + ValueFromBlock notFoundResult = isArrayIncludes ? m_out.anchor(m_out.constBool(false)) : m_out.anchor(m_out.constIntPtr(-1)); m_out.jump(continuation); m_out.appendTo(slowCase, continuation); - ValueFromBlock slowCaseResult = m_out.anchor(vmCall(Int64, operationArrayIndexOfString, weakPointer(globalObject), storage, searchElement, startIndex)); + ValueFromBlock slowCaseResult = isArrayIncludes ? m_out.anchor(vmCall(Int32, operationArrayIncludesString, weakPointer(globalObject), storage, searchElement, startIndex)) : m_out.anchor(vmCall(Int64, operationArrayIndexOfString, weakPointer(globalObject), storage, searchElement, startIndex)); m_out.jump(continuation); m_out.appendTo(continuation, lastNext); // We have to keep base alive since that keeps content of storage alive. ensureStillAliveHere(base); - setInt32(m_out.castToInt32(m_out.phi(Int64, notFoundResult, foundResult, slowCaseResult))); + if (isArrayIncludes) + setBoolean(m_out.phi(Int32, notFoundResult, foundResult, slowCaseResult)); + else + setInt32(m_out.castToInt32(m_out.phi(Int64, notFoundResult, foundResult, slowCaseResult))); return; } @@ -7809,21 +7820,30 @@ IGNORE_CLANG_WARNINGS_END RELEASE_ASSERT_NOT_REACHED(); break; } - setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfNonStringIdentityValueContiguous, storage, searchElement, startIndex))); + if (isArrayIncludes) + setBoolean(vmCall(Int32, operationArrayIncludesNonStringIdentityValueContiguous, storage, searchElement, startIndex)); + else + setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfNonStringIdentityValueContiguous, storage, searchElement, startIndex))); return; } case UntypedUse: switch (m_node->arrayMode().type()) { case Array::Double: - setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfValueDouble, storage, lowJSValue(searchElementEdge), startIndex))); + if (isArrayIncludes) + setBoolean(vmCall(Int32, operationArrayIncludesValueDouble, storage, lowJSValue(searchElementEdge), startIndex)); + else + setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfValueDouble, storage, lowJSValue(searchElementEdge), startIndex))); return; case Array::Contiguous: // We have to keep base alive since that keeps content of storage alive. ensureStillAliveHere(base); FALLTHROUGH; case Array::Int32: - setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfValueInt32OrContiguous, weakPointer(globalObject), storage, lowJSValue(searchElementEdge), startIndex))); + if (isArrayIncludes) + setBoolean(vmCall(Int32, operationArrayIncludesValueInt32OrContiguous, weakPointer(globalObject), storage, lowJSValue(searchElementEdge), startIndex)); + else + setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfValueInt32OrContiguous, weakPointer(globalObject), storage, lowJSValue(searchElementEdge), startIndex))); return; default: RELEASE_ASSERT_NOT_REACHED(); diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp index b6b5404c8949d..64daf2e3b52d6 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -68,6 +68,7 @@ static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncFill); static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncToReversed); static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncToSorted); static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncWith); +static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncIncludes); // ------------------------------ ArrayPrototype ---------------------------- @@ -125,7 +126,7 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().findLastPublicName(), arrayPrototypeFindLastCodeGenerator, static_cast(PropertyAttribute::DontEnum)); JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().findIndexPublicName(), arrayPrototypeFindIndexCodeGenerator, static_cast(PropertyAttribute::DontEnum)); JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().findLastIndexPublicName(), arrayPrototypeFindLastIndexCodeGenerator, static_cast(PropertyAttribute::DontEnum)); - JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().includesPublicName(), arrayPrototypeIncludesCodeGenerator, static_cast(PropertyAttribute::DontEnum)); + JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->includes, arrayProtoFuncIncludes, static_cast(PropertyAttribute::DontEnum), 1, ImplementationVisibility::Public, ArrayIncludesIntrinsic); JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().copyWithinPublicName(), arrayPrototypeCopyWithinCodeGenerator, static_cast(PropertyAttribute::DontEnum)); JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().atPublicName(), arrayPrototypeAtCodeGenerator, static_cast(PropertyAttribute::DontEnum)); JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toReversed, arrayProtoFuncToReversed, static_cast(PropertyAttribute::DontEnum), 0, ImplementationVisibility::Public); @@ -134,7 +135,7 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->with, arrayProtoFuncWith, static_cast(PropertyAttribute::DontEnum), 2, ImplementationVisibility::Public); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().entriesPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().entriesPublicName()), static_cast(PropertyAttribute::ReadOnly)); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().forEachPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().forEachPublicName()), static_cast(PropertyAttribute::ReadOnly)); - putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().includesPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().includesPublicName()), static_cast(PropertyAttribute::ReadOnly)); + putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().includesPrivateName(), getDirect(vm, vm.propertyNames->includes), static_cast(PropertyAttribute::ReadOnly)); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().indexOfPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().indexOfPublicName()), static_cast(PropertyAttribute::ReadOnly)); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().keysPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().keysPublicName()), static_cast(PropertyAttribute::ReadOnly)); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().mapPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().mapPublicName()), static_cast(PropertyAttribute::ReadOnly)); @@ -154,7 +155,7 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) &vm.propertyNames->builtinNames().findLastIndexPublicName(), &vm.propertyNames->builtinNames().flatPublicName(), &vm.propertyNames->builtinNames().flatMapPublicName(), - &vm.propertyNames->builtinNames().includesPublicName(), + &vm.propertyNames->includes, &vm.propertyNames->builtinNames().keysPublicName(), &vm.propertyNames->toReversed, &vm.propertyNames->toSorted, @@ -2126,6 +2127,50 @@ JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncWith, (JSGlobalObject* globalObject, Call return JSValue::encode(result); } +JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncIncludes, (JSGlobalObject* globalObject, CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()); + RETURN_IF_EXCEPTION(scope, { }); + + if (UNLIKELY(thisValue.isUndefinedOrNull())) + return throwVMTypeError(globalObject, scope, "Array.prototype.includes requires that |this| not be null or undefined"_s); + auto* thisObject = thisValue.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, { }); + + uint64_t length = toLength(globalObject, thisObject); + RETURN_IF_EXCEPTION(scope, { }); + + if (!length) + return JSValue::encode(jsBoolean(false)); + + uint64_t index = argumentClampedIndexFromStartOrEnd(globalObject, callFrame->argument(1), length, 0); + RETURN_IF_EXCEPTION(scope, { }); + + ASSERT(index <= length); + if (index == length) + return JSValue::encode(jsBoolean(false)); + + JSValue searchElement = callFrame->argument(0); + + if (LIKELY(isJSArray(thisObject))) { + JSArray* thisArray = jsCast(thisObject); + if (auto fastResult = thisArray->fastIncludes(globalObject, searchElement, index, length)) + return JSValue::encode(jsBoolean(fastResult.value())); + } + + for (; index < length; ++index) { + auto currentElement = thisObject->getIndex(globalObject, index); + RETURN_IF_EXCEPTION(scope, { }); + if (sameValueZero(globalObject, searchElement, currentElement)) + return JSValue::encode(jsBoolean(true)); + } + + return JSValue::encode(jsBoolean(false)); +} + } // namespace JSC WTF_ALLOW_UNSAFE_BUFFER_USAGE_END diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h index 6bfa977720646..5165f0f3565e0 100644 --- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h +++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h @@ -161,6 +161,7 @@ macro(id) \ macro(ignoreCase) \ macro(ignorePunctuation) \ + macro(includes) \ macro(index) \ macro(indices) \ macro(inferredName) \ diff --git a/Source/JavaScriptCore/runtime/Intrinsic.h b/Source/JavaScriptCore/runtime/Intrinsic.h index 4b886f779cc26..220ae250b6d61 100644 --- a/Source/JavaScriptCore/runtime/Intrinsic.h +++ b/Source/JavaScriptCore/runtime/Intrinsic.h @@ -56,6 +56,7 @@ namespace JSC { macro(ArrayPopIntrinsic) \ macro(ArraySliceIntrinsic) \ macro(ArraySpliceIntrinsic) \ + macro(ArrayIncludesIntrinsic) \ macro(ArrayIndexOfIntrinsic) \ macro(ArrayValuesIntrinsic) \ macro(ArrayKeysIntrinsic) \ diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index f5e157840f99b..605c92520530c 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -734,6 +734,91 @@ JSArray* JSArray::fastWith(JSGlobalObject* globalObject, uint32_t index, JSValue } } +std::optional JSArray::fastIncludes(JSGlobalObject* globalObject, JSValue searchElement, uint64_t index64, uint64_t length64) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + bool canDoFastPath = this->canDoFastIndexedAccess() + && this->getArrayLength() == length64 // The effects in getting `index` could have changed the length of this array. + && static_cast(index64) == index64; + if (!canDoFastPath) + return std::nullopt; + + uint32_t length = static_cast(length64); + uint32_t index = static_cast(index64); + + switch (this->indexingType()) { + case ArrayWithInt32: { + auto& butterfly = *this->butterfly(); + auto data = butterfly.contiguous().data(); + + if (searchElement.isUndefined()) + return containsHole(data, length64); + if (!searchElement.isNumber()) + return false; + JSValue searchInt32; + if (searchElement.isInt32()) + searchInt32 = searchElement; + else { + double searchNumber = searchElement.asNumber(); + if (!canBeInt32(searchNumber)) + return false; + searchInt32 = jsNumber(static_cast(searchNumber)); + } + for (; index < length; ++index) { + if (searchInt32 == data[index].get()) + return true; + } + return false; + } + case ArrayWithContiguous: { + auto& butterfly = *this->butterfly(); + auto data = butterfly.contiguous().data(); + + if (searchElement.isObject()) { + auto* result = std::bit_cast*>(WTF::find64(std::bit_cast(data + index), JSValue::encode(searchElement), length - index)); + if (result) + return true; + return false; + } + + bool searchElementIsUndefined = searchElement.isUndefined(); + for (; index < length; ++index) { + JSValue value = data[index].get(); + if (!value) { + if (searchElementIsUndefined) + return true; + continue; + } + bool isEqual = sameValueZero(globalObject, searchElement, value); + RETURN_IF_EXCEPTION(scope, { }); + if (isEqual) + return true; + } + return false; + } + case ALL_DOUBLE_INDEXING_TYPES: { + auto& butterfly = *this->butterfly(); + auto data = butterfly.contiguousDouble().data(); + + if (searchElement.isUndefined()) + return containsHole(data, length64); + if (!searchElement.isNumber()) + return false; + + double searchNumber = searchElement.asNumber(); + for (; index < length; ++index) { + if (data[index] == searchNumber) + return true; + } + return false; + } + default: + return std::nullopt; + } +} + bool JSArray::appendMemcpy(JSGlobalObject* globalObject, VM& vm, unsigned startIndex, IndexingType otherType, std::span values) { auto scope = DECLARE_THROW_SCOPE(vm); diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h index e26dac32883f9..9ad15922ad58d 100644 --- a/Source/JavaScriptCore/runtime/JSArray.h +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -125,6 +125,8 @@ class JSArray : public JSNonFinalObject { JSArray* fastWith(JSGlobalObject*, uint32_t index, JSValue, uint64_t length); + std::optional fastIncludes(JSGlobalObject*, JSValue, uint64_t fromIndex, uint64_t length); + ALWAYS_INLINE bool definitelyNegativeOneMiss() const; enum ShiftCountMode { diff --git a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h index 3010335e0101b..7d6b82faf7203 100644 --- a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h +++ b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h @@ -1479,4 +1479,26 @@ ALWAYS_INLINE bool sameValue(JSGlobalObject* globalObject, JSValue a, JSValue b) return std::bit_cast(x) == std::bit_cast(y); } +ALWAYS_INLINE bool sameValueZero(JSGlobalObject* globalObject, JSValue a, JSValue b) +{ + if (a == b) + return true; + + if (!a.isNumber()) + return JSValue::strictEqual(globalObject, a, b); + if (!b.isNumber()) + return false; + double x = a.asNumber(); + double y = b.asNumber(); + if (std::isnan(x)) + return std::isnan(y); + if (std::isnan(y)) + return std::isnan(x); + if (!x && y == -0) + return true; + if (x == -0 && !y) + return true; + return std::bit_cast(x) == std::bit_cast(y); +} + } // namespace JSC From 6e8fef40092d06a84de870150952f169c94bd953 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sun, 23 Feb 2025 00:24:21 -0800 Subject: [PATCH 086/174] [JSC] Close Iterator Helpers underlying iterator when arguments are invalid https://bugs.webkit.org/show_bug.cgi?id=284709 Reviewed by Yusuke Suzuki. The normative change[1] that requires closing underlying iterators when argument validation fails for iterator helpers reached consensus at the TC39 meeting in December 2024[2]. This patch implements it. [1]: https://github.com/tc39/ecma262/pull/3467 [2]: https://github.com/tc39/agendas/blob/main/2024/12.md * JSTests/stress/iterator-helpers-close-for-invalid-argument.js: Added. (shouldThrow): (shouldBe): (throw.new.Error.let.closable.get next): (throw.new.Error): (shouldBe.let.closable.get next): (shouldBe.OurError): (let.closable.get next): (shouldBe.get shouldBe): (OurError): (get shouldBe): * Source/JavaScriptCore/builtins/JSIteratorPrototype.js: (some.wrapper.iterator): (some): (every.wrapper.iterator): (every): (find.wrapper.iterator): (find): (reduce): Canonical link: https://commits.webkit.org/290907@main --- ...ator-helpers-close-for-invalid-argument.js | 296 ++++++++++++++++++ JSTests/test262/expectations.yaml | 30 -- .../builtins/JSIteratorPrototype.js | 64 ++-- .../runtime/JSIteratorPrototype.cpp | 5 +- 4 files changed, 344 insertions(+), 51 deletions(-) create mode 100644 JSTests/stress/iterator-helpers-close-for-invalid-argument.js diff --git a/JSTests/stress/iterator-helpers-close-for-invalid-argument.js b/JSTests/stress/iterator-helpers-close-for-invalid-argument.js new file mode 100644 index 0000000000000..9f5b0d0ca8605 --- /dev/null +++ b/JSTests/stress/iterator-helpers-close-for-invalid-argument.js @@ -0,0 +1,296 @@ +function shouldThrow(errorType, func) { + let error; + try { + func(); + } catch (e) { + error = e; + } + if (!(error instanceof errorType)) { + print(error.message); + throw new Error(`Expected ${errorType.name}! got ${error.name}`); + } +} + +function shouldBe(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +{ + // Iterator.prototype.map + let closed = false; + let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, + }; + shouldThrow(TypeError, function() { + closable.map(); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(TypeError, function() { + closable.map({}); + }); + shouldBe(closed, true); +} + +{ + // Iterator.prototype.filter + let closed = false; + let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, + }; + shouldThrow(TypeError, function() { + closable.filter(); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(TypeError, function() { + closable.filter({}); + }); + shouldBe(closed, true); +} + +{ + // Iterator.prototype.take + let closed = false; + let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, + }; + + shouldThrow(RangeError, function() { + closable.take(); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(RangeError, function() { + closable.take(NaN); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(RangeError, function() { + closable.take(-1); + }); + shouldBe(closed, true); + + closed = false; + function OurError() {} + shouldThrow(OurError, function() { + closable.take({ get valueOf() { throw new OurError(); }}); + }); + shouldBe(closed, true); +} + +{ + // Iterator.prototype.drop + let closed = false; + let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, + }; + + shouldThrow(RangeError, function() { + closable.drop(); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(RangeError, function() { + closable.drop(NaN); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(RangeError, function() { + closable.drop(-1); + }); + shouldBe(closed, true); + + closed = false; + function OurError() {} + shouldThrow(OurError, function() { + closable.drop({ get valueOf() { throw new OurError(); }}); + }); + shouldBe(closed, true); +} + +{ + // Iterator.prototype.flatMap + let closed = false; + let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, + }; + shouldThrow(TypeError, function() { + closable.flatMap(); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(TypeError, function() { + closable.flatMap({}); + }); + shouldBe(closed, true); +} + +{ + // Iterator.prototype.some + let closed = false; + let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, + }; + shouldThrow(TypeError, function() { + closable.some(); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(TypeError, function() { + closable.some({}); + }); + shouldBe(closed, true); +} + +{ + // Iterator.prototype.every + let closed = false; + let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, + }; + shouldThrow(TypeError, function() { + closable.every(); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(TypeError, function() { + closable.every({}); + }); + shouldBe(closed, true); +} + +{ + // Iterator.prototype.find + let closed = false; + let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, + }; + shouldThrow(TypeError, function() { + closable.find(); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(TypeError, function() { + closable.find({}); + }); + shouldBe(closed, true); +} + +{ + // Iterator.prototype.reduce + let closed = false; + let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, + }; + shouldThrow(TypeError, function() { + closable.reduce(); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(TypeError, function() { + closable.reduce({}); + }); + shouldBe(closed, true); +} + +{ + // Iterator.prototype.forEach + let closed = false; + let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, + }; + shouldThrow(TypeError, function() { + closable.forEach(); + }); + shouldBe(closed, true); + + closed = false; + shouldThrow(TypeError, function() { + closable.forEach({}); + }); + shouldBe(closed, true); +} + diff --git a/JSTests/test262/expectations.yaml b/JSTests/test262/expectations.yaml index cc3b310653991..9fa3050af5f96 100644 --- a/JSTests/test262/expectations.yaml +++ b/JSTests/test262/expectations.yaml @@ -25,36 +25,6 @@ test/built-ins/Iterator/concat/fresh-iterator-result.js: test/built-ins/Iterator/concat/next-method-returns-throwing-value.js: default: 'Test262Error: ' strict mode: 'Test262Error: ' -test/built-ins/Iterator/prototype/drop/argument-validation-failure-closes-underlying.js: - default: 'Test262Error: Expected SameValue(«false», «true») to be true' - strict mode: 'Test262Error: Expected SameValue(«false», «true») to be true' -test/built-ins/Iterator/prototype/every/argument-validation-failure-closes-underlying.js: - default: 'Test262Error: Expected SameValue(«false», «true») to be true' - strict mode: 'Test262Error: Expected SameValue(«false», «true») to be true' -test/built-ins/Iterator/prototype/filter/argument-validation-failure-closes-underlying.js: - default: 'Test262Error: Expected SameValue(«false», «true») to be true' - strict mode: 'Test262Error: Expected SameValue(«false», «true») to be true' -test/built-ins/Iterator/prototype/find/argument-validation-failure-closes-underlying.js: - default: 'Test262Error: Expected SameValue(«false», «true») to be true' - strict mode: 'Test262Error: Expected SameValue(«false», «true») to be true' -test/built-ins/Iterator/prototype/flatMap/argument-validation-failure-closes-underlying.js: - default: 'Test262Error: Expected SameValue(«false», «true») to be true' - strict mode: 'Test262Error: Expected SameValue(«false», «true») to be true' -test/built-ins/Iterator/prototype/forEach/argument-validation-failure-closes-underlying.js: - default: 'Test262Error: Expected SameValue(«false», «true») to be true' - strict mode: 'Test262Error: Expected SameValue(«false», «true») to be true' -test/built-ins/Iterator/prototype/map/argument-validation-failure-closes-underlying.js: - default: 'Test262Error: Expected SameValue(«false», «true») to be true' - strict mode: 'Test262Error: Expected SameValue(«false», «true») to be true' -test/built-ins/Iterator/prototype/reduce/argument-validation-failure-closes-underlying.js: - default: 'Test262Error: Expected SameValue(«false», «true») to be true' - strict mode: 'Test262Error: Expected SameValue(«false», «true») to be true' -test/built-ins/Iterator/prototype/some/argument-validation-failure-closes-underlying.js: - default: 'Test262Error: Expected SameValue(«false», «true») to be true' - strict mode: 'Test262Error: Expected SameValue(«false», «true») to be true' -test/built-ins/Iterator/prototype/take/argument-validation-failure-closes-underlying.js: - default: 'Test262Error: Expected SameValue(«false», «true») to be true' - strict mode: 'Test262Error: Expected SameValue(«false», «true») to be true' test/built-ins/Object/entries/order-after-define-property-with-function.js: default: 'Test262Error: Actual [a, name] and expected [name, a] should have the same contents. ' strict mode: 'Test262Error: Actual [a, name] and expected [name, a] should have the same contents. ' diff --git a/Source/JavaScriptCore/builtins/JSIteratorPrototype.js b/Source/JavaScriptCore/builtins/JSIteratorPrototype.js index d3bb67d5657e8..113612a26be25 100644 --- a/Source/JavaScriptCore/builtins/JSIteratorPrototype.js +++ b/Source/JavaScriptCore/builtins/JSIteratorPrototype.js @@ -32,8 +32,10 @@ function map(mapper) if (!@isObject(this)) @throwTypeError("Iterator.prototype.map requires that |this| be an Object."); - if (!@isCallable(mapper)) + if (!@isCallable(mapper)) { + @iteratorGenericClose(this); @throwTypeError("Iterator.prototype.map callback must be a function."); + } var iterated = this; var iteratedNextMethod = iterated.next; @@ -63,8 +65,10 @@ function filter(predicate) if (!@isObject(this)) @throwTypeError("Iterator.prototype.filter requires that |this| be an Object."); - if (!@isCallable(predicate)) + if (!@isCallable(predicate)) { + @iteratorGenericClose(this); @throwTypeError("Iterator.prototype.filter callback must be a function."); + } var iterated = this; var iteratedNextMethod = iterated.next; @@ -94,13 +98,18 @@ function take(limit) if (!@isObject(this)) @throwTypeError("Iterator.prototype.take requires that |this| be an Object."); - var numLimit = @toNumber(limit); - if (numLimit !== numLimit) + var numLimit; + @ifAbruptCloseIterator(this, numLimit = @toNumber(limit)); + if (numLimit !== numLimit) { + @iteratorGenericClose(this); @throwRangeError("Iterator.prototype.take argument must not be NaN."); + } var intLimit = @toIntegerOrInfinity(numLimit); - if (intLimit < 0) + if (intLimit < 0) { + @iteratorGenericClose(this); @throwRangeError("Iterator.prototype.take argument must be non-negative."); + } var iterated = this; var iteratedNextMethod = iterated.next; @@ -138,13 +147,18 @@ function drop(limit) if (!@isObject(this)) @throwTypeError("Iterator.prototype.drop requires that |this| be an Object."); - var numLimit = @toNumber(limit); - if (numLimit !== numLimit) + var numLimit; + @ifAbruptCloseIterator(this, (numLimit = @toNumber(limit))); + if (numLimit !== numLimit) { + @iteratorGenericClose(this); @throwRangeError("Iterator.prototype.drop argument must not be NaN."); + } var intLimit = @toIntegerOrInfinity(numLimit); - if (intLimit < 0) + if (intLimit < 0) { + @iteratorGenericClose(this); @throwRangeError("Iterator.prototype.drop argument must be non-negative."); + } var iterated = this; var iteratedNextMethod = iterated.next; @@ -179,8 +193,10 @@ function flatMap(mapper) if (!@isObject(this)) @throwTypeError("Iterator.prototype.flatMap requires that |this| be an Object."); - if (!@isCallable(mapper)) + if (!@isCallable(mapper)) { + @iteratorGenericClose(this); @throwTypeError("Iterator.prototype.flatMap callback must be a function."); + } var iterated = this; var iteratedNextMethod = iterated.next; @@ -212,12 +228,14 @@ function some(predicate) if (!@isObject(this)) @throwTypeError("Iterator.prototype.some requires that |this| be an Object."); - if (!@isCallable(predicate)) + if (!@isCallable(predicate)) { + @iteratorGenericClose(this); @throwTypeError("Iterator.prototype.some callback must be a function."); + } + var iterated = this; var count = 0; - var iterator = this; - var wrapper = { @@iterator: function () { return iterator; }}; + var wrapper = { @@iterator: function () { return iterated; }}; for (var item of wrapper) { if (predicate(item, count++)) return true; @@ -234,12 +252,14 @@ function every(predicate) if (!@isObject(this)) @throwTypeError("Iterator.prototype.every requires that |this| be an Object."); - if (!@isCallable(predicate)) + if (!@isCallable(predicate)) { + @iteratorGenericClose(this); @throwTypeError("Iterator.prototype.every callback must be a function."); + } + var iterated = this; var count = 0; - var iterator = this; - var wrapper = { @@iterator: function () { return iterator; }}; + var wrapper = { @@iterator: function () { return iterated; }}; for (var item of wrapper) { if (!predicate(item, count++)) return false; @@ -256,12 +276,14 @@ function find(predicate) if (!@isObject(this)) @throwTypeError("Iterator.prototype.find requires that |this| be an Object."); - if (!@isCallable(predicate)) + if (!@isCallable(predicate)) { + @iteratorGenericClose(this); @throwTypeError("Iterator.prototype.find callback must be a function."); + } + var iterated = this; var count = 0; - var iterator = this; - var wrapper = { @@iterator: function () { return iterator; }}; + var wrapper = { @@iterator: function () { return iterated; }}; for (var item of wrapper) { if (predicate(item, count++)) return item; @@ -278,12 +300,14 @@ function reduce(reducer /*, initialValue */) if (!@isObject(this)) @throwTypeError("Iterator.prototype.reduce requires that |this| be an Object."); - if (!@isCallable(reducer)) + if (!@isCallable(reducer)) { + @iteratorGenericClose(this); @throwTypeError("Iterator.prototype.reduce reducer argument must be a function."); + } + var iterated = this; var initialValue = @argument(1); - var iterated = this; var iteratedNextMethod = this.next; var accumulator; diff --git a/Source/JavaScriptCore/runtime/JSIteratorPrototype.cpp b/Source/JavaScriptCore/runtime/JSIteratorPrototype.cpp index 26a2501bbfb76..c4d1a2d5820c2 100644 --- a/Source/JavaScriptCore/runtime/JSIteratorPrototype.cpp +++ b/Source/JavaScriptCore/runtime/JSIteratorPrototype.cpp @@ -162,8 +162,11 @@ JSC_DEFINE_HOST_FUNCTION(iteratorProtoFuncForEach, (JSGlobalObject* globalObject return throwVMTypeError(globalObject, scope, "Iterator.prototype.forEach requires that |this| be an Object."_s); JSValue callbackArg = callFrame->argument(0); - if (!callbackArg.isCallable()) + if (!callbackArg.isCallable()) { + iteratorClose(globalObject, thisValue); + RETURN_IF_EXCEPTION(scope, { }); return throwVMTypeError(globalObject, scope, "Iterator.prototype.forEach requires the callback argument to be callable."_s); + } auto callData = JSC::getCallData(callbackArg); ASSERT(callData.type != CallData::Type::None); From 1b9a2025ca119638448bfa4fc1e3b7875a1d4a81 Mon Sep 17 00:00:00 2001 From: Tyler Wilcock Date: Sun, 23 Feb 2025 08:27:21 -0800 Subject: [PATCH 087/174] AX: With ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE) we need to eagerly recompute is-ignored for descendants of aria-hidden elements https://bugs.webkit.org/show_bug.cgi?id=288299 rdar://145387158 Reviewed by Chris Fleizach. Prior to ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE), we handled dynamic aria-hidden changes with AXObjectCache::childrenChanged. However, with ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE), there are no actual accessibility tree changes, just different is-ignored values for the subtree. This fixes these two tests: - accessibility/aria-hidden-updates-alldescendants.html - accessibility/mac/iframe-aria-hidden.html This commit also fixes an existing bug exposed by ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE) where we don't process childrenChanged events when the elements filling a slot change, which impacts the accessibility tree. This fixes accessibility/mac/invalid-summary-element.html. Finally, we fix accessibility/details-summary-content-hidden.html by making the test more async. * LayoutTests/accessibility/details-summary-content-hidden.html: * Source/WebCore/accessibility/AXObjectCache.cpp: (WebCore::AXObjectCache::onSlottedContentChange): (WebCore::AXObjectCache::handleAttributeChange): * Source/WebCore/accessibility/AXObjectCache.h: * Source/WebCore/accessibility/AccessibilityRenderObject.cpp: (WebCore::AccessibilityRenderObject::addNodeOnlyChildren): * Source/WebCore/dom/SlotAssignment.cpp: (WebCore::NamedSlotAssignment::didChangeSlot): * Source/WebCore/html/HTMLSlotElement.cpp: (WebCore::HTMLSlotElement::updateAccessibilityOnSlotChange const): * Source/WebCore/html/HTMLSlotElement.h: Canonical link: https://commits.webkit.org/290908@main --- .../details-summary-content-hidden.html | 2 +- Source/WebCore/accessibility/AXObjectCache.cpp | 14 ++++++++++++++ Source/WebCore/accessibility/AXObjectCache.h | 1 + .../accessibility/AccessibilityRenderObject.cpp | 2 +- Source/WebCore/dom/SlotAssignment.cpp | 2 ++ Source/WebCore/html/HTMLSlotElement.cpp | 6 ++++++ Source/WebCore/html/HTMLSlotElement.h | 1 + 7 files changed, 26 insertions(+), 2 deletions(-) diff --git a/LayoutTests/accessibility/details-summary-content-hidden.html b/LayoutTests/accessibility/details-summary-content-hidden.html index ebde88a63c8f6..740675c6aeb25 100644 --- a/LayoutTests/accessibility/details-summary-content-hidden.html +++ b/LayoutTests/accessibility/details-summary-content-hidden.html @@ -33,7 +33,7 @@ document.getElementById("details").removeAttribute("open"); output += await expectAsync("details.isExpanded", "false"); output += await expectAsync("summary.isExpanded", "false"); - output += expect("!accessibilityController.accessibleElementById('content')", "true"); + output += await expectAsync("!accessibilityController.accessibleElementById('content')", "true"); debug(output); finishJSTest(); diff --git a/Source/WebCore/accessibility/AXObjectCache.cpp b/Source/WebCore/accessibility/AXObjectCache.cpp index db3ecfc068853..bdd3887ec5934 100644 --- a/Source/WebCore/accessibility/AXObjectCache.cpp +++ b/Source/WebCore/accessibility/AXObjectCache.cpp @@ -1956,6 +1956,11 @@ void AXObjectCache::onSelectedChanged(Element& element) handleTabPanelSelected(nullptr, &element); } +void AXObjectCache::onSlottedContentChange(const HTMLSlotElement& slot) +{ + childrenChanged(get(const_cast(slot))); +} + void AXObjectCache::onStyleChange(Element& element, Style::Change change, const RenderStyle* oldStyle, const RenderStyle* newStyle) { if (change == Style::Change::None || !oldStyle || !newStyle) @@ -2868,8 +2873,17 @@ void AXObjectCache::handleAttributeChange(Element* element, const QualifiedName& else if (attrName == aria_haspopupAttr) postNotification(element, AXNotification::HasPopupChanged); else if (attrName == aria_hiddenAttr) { +#if ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE) + if (RefPtr axObject = getOrCreate(*element)) { + Accessibility::enumerateDescendantsIncludingIgnored(*axObject, /* includeSelf */ true, [] (auto& descendant) { + downcast(descendant).recomputeIsIgnored(); + }); + } +#else if (RefPtr parent = get(element->parentNode())) childrenChanged(parent.get()); +#endif // ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE) + if (m_currentModalElement && m_currentModalElement->isDescendantOf(element)) deferModalChange(*m_currentModalElement); diff --git a/Source/WebCore/accessibility/AXObjectCache.h b/Source/WebCore/accessibility/AXObjectCache.h index 2be583a117f8a..78ed83fdcf8b0 100644 --- a/Source/WebCore/accessibility/AXObjectCache.h +++ b/Source/WebCore/accessibility/AXObjectCache.h @@ -362,6 +362,7 @@ class AXObjectCache final : public CanMakeWeakPtr, public CanMake void onPopoverToggle(const HTMLElement&); void onScrollbarFrameRectChange(const Scrollbar&); void onSelectedChanged(Element&); + void onSlottedContentChange(const HTMLSlotElement&); void onStyleChange(Element&, Style::Change, const RenderStyle* oldStyle, const RenderStyle* newStyle); void onStyleChange(RenderText&, StyleDifference, const RenderStyle* oldStyle, const RenderStyle& newStyle); void onTextSecurityChanged(HTMLInputElement&); diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp index 62e414e773353..5d2dcc55fbc13 100644 --- a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp @@ -2434,7 +2434,7 @@ void AccessibilityRenderObject::addNodeOnlyChildren() WeakPtr cache = axObjectCache(); if (!cache) return; - // FIXME: This algorithm does not work correctly when ENABLE(INCLUDE_IGNORED_IN_CORE_TREE) due to use of m_children, as this algorithm is written assuming m_children only every contains unignored objects. + // FIXME: This algorithm does not work correctly when ENABLE(INCLUDE_IGNORED_IN_CORE_AX_TREE) due to use of m_children, as this algorithm is written assuming m_children only every contains unignored objects. // Iterate through all of the children, including those that may have already been added, and // try to insert the nodes in the correct place in the DOM order. unsigned insertionIndex = 0; diff --git a/Source/WebCore/dom/SlotAssignment.cpp b/Source/WebCore/dom/SlotAssignment.cpp index e27a1059bcba0..e8fec25e98736 100644 --- a/Source/WebCore/dom/SlotAssignment.cpp +++ b/Source/WebCore/dom/SlotAssignment.cpp @@ -322,6 +322,8 @@ void NamedSlotAssignment::didChangeSlot(const AtomString& slotAttrValue, ShadowR if (slotElement->selfOrPrecedingNodesAffectDirAuto()) slotElement->updateEffectiveTextDirection(); + + slotElement->updateAccessibilityOnSlotChange(); } void NamedSlotAssignment::didRemoveAllChildrenOfShadowHost(ShadowRoot& shadowRoot) diff --git a/Source/WebCore/html/HTMLSlotElement.cpp b/Source/WebCore/html/HTMLSlotElement.cpp index d0ec3018ac38b..7a7a7791f5fe8 100644 --- a/Source/WebCore/html/HTMLSlotElement.cpp +++ b/Source/WebCore/html/HTMLSlotElement.cpp @@ -232,4 +232,10 @@ void HTMLSlotElement::dispatchSlotChangeEvent() dispatchEvent(event); } +void HTMLSlotElement::updateAccessibilityOnSlotChange() const +{ + if (CheckedPtr cache = protectedDocument()->existingAXObjectCache()) + cache->onSlottedContentChange(*this); +} + } // namespace WebCore diff --git a/Source/WebCore/html/HTMLSlotElement.h b/Source/WebCore/html/HTMLSlotElement.h index 408428de7b588..defc4429b13fa 100644 --- a/Source/WebCore/html/HTMLSlotElement.h +++ b/Source/WebCore/html/HTMLSlotElement.h @@ -55,6 +55,7 @@ class HTMLSlotElement final : public HTMLElement { bool isInInsertedIntoAncestor() const { return m_isInInsertedIntoAncestor; } + void updateAccessibilityOnSlotChange() const; private: HTMLSlotElement(const QualifiedName&, Document&); From c3154758f699e01b87a4f519d000c05dc2bf0ecd Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 23 Feb 2025 09:39:49 -0800 Subject: [PATCH 088/174] [css-anchor-position-1] Implement parsing `anchor-scope` rdar://145016336 https://bugs.webkit.org/show_bug.cgi?id=287835 Reviewed by Tim Nguyen. * LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/anchor-scope-computed-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/anchor-scope-parsing-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/anchor-scope-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/anchor-scope.html: * LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/accumulation-per-property-001-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/addition-per-property-001-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-001-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/property-list.js: * LayoutTests/platform/glib/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt: * LayoutTests/platform/ios/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt: * LayoutTests/platform/ipad/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt: * LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt: * Source/WebCore/Headers.cmake: * Source/WebCore/WebCore.xcodeproj/project.pbxproj: * Source/WebCore/animation/CSSPropertyAnimation.cpp: (WebCore::CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap): * Source/WebCore/css/CSSProperties.json: * Source/WebCore/css/ComputedStyleExtractor.cpp: (WebCore::valueForNameScope): (WebCore::ComputedStyleExtractor::valueForPropertyInStyle const): (WebCore::valueForNameScopeNames): Deleted. * Source/WebCore/rendering/style/NameScope.h: Renamed from Source/WebCore/css/NameScope.h. * Source/WebCore/rendering/style/RenderStyle.cpp: (WebCore::RenderStyle::conservativelyCollectChangedAnimatableProperties const): * Source/WebCore/rendering/style/RenderStyle.h: * Source/WebCore/rendering/style/RenderStyleInlines.h: (WebCore::RenderStyle::anchorScope const): (WebCore::RenderStyle::initialAnchorScope): * Source/WebCore/rendering/style/RenderStyleSetters.h: (WebCore::RenderStyle::setAnchorScope): * Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp: (WebCore::StyleRareNonInheritedData::StyleRareNonInheritedData): (WebCore::StyleRareNonInheritedData::operator== const): (WebCore::StyleRareNonInheritedData::dumpDifferences const): * Source/WebCore/rendering/style/StyleRareNonInheritedData.h: Canonical link: https://commits.webkit.org/290909@main --- .../anchor-scope-computed-expected.txt | 16 ++++---- .../parsing/anchor-scope-parsing-expected.txt | 18 ++++----- .../all-prop-initial-xml-expected.txt | 1 + .../properties/anchor-scope-expected.txt | 18 ++++----- .../properties/anchor-scope.html | 3 +- ...accumulation-per-property-001-expected.txt | 3 ++ .../addition-per-property-001-expected.txt | 3 ++ ...nterpolation-per-property-001-expected.txt | 4 ++ .../animation-types/property-list.js | 6 +++ .../all-prop-initial-xml-expected.txt | 1 + .../all-prop-initial-xml-expected.txt | 1 + .../all-prop-initial-xml-expected.txt | 1 + .../all-prop-initial-xml-expected.txt | 1 + Source/WebCore/Headers.cmake | 2 +- .../WebCore/WebCore.xcodeproj/project.pbxproj | 4 +- .../animation/CSSPropertyAnimation.cpp | 1 + Source/WebCore/css/CSSProperties.json | 13 ++++++ Source/WebCore/css/ComputedStyleExtractor.cpp | 40 +++++++++++-------- .../{css => rendering/style}/NameScope.h | 0 .../WebCore/rendering/style/RenderStyle.cpp | 2 + Source/WebCore/rendering/style/RenderStyle.h | 4 ++ .../rendering/style/RenderStyleInlines.h | 2 + .../rendering/style/RenderStyleSetters.h | 1 + .../style/StyleRareNonInheritedData.cpp | 4 ++ .../style/StyleRareNonInheritedData.h | 1 + 25 files changed, 103 insertions(+), 47 deletions(-) rename Source/WebCore/{css => rendering/style}/NameScope.h (100%) diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/anchor-scope-computed-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/anchor-scope-computed-expected.txt index dd7ad4e07956f..1ee8fd1c6a178 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/anchor-scope-computed-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/anchor-scope-computed-expected.txt @@ -1,10 +1,10 @@ -FAIL Property anchor-scope value 'none' assert_true: anchor-scope doesn't seem to be supported in the computed style expected true got false -FAIL Property anchor-scope value 'initial' assert_true: anchor-scope doesn't seem to be supported in the computed style expected true got false -FAIL Property anchor-scope value 'all' assert_true: anchor-scope doesn't seem to be supported in the computed style expected true got false -FAIL Property anchor-scope value '--a' assert_true: anchor-scope doesn't seem to be supported in the computed style expected true got false -FAIL Property anchor-scope value '--a, --b' assert_true: anchor-scope doesn't seem to be supported in the computed style expected true got false -FAIL Property anchor-scope value '--a, --b, --c' assert_true: anchor-scope doesn't seem to be supported in the computed style expected true got false -FAIL Property anchor-scope has initial value none assert_true: anchor-scope doesn't seem to be supported in the computed style expected true got false -FAIL Property anchor-scope does not inherit assert_true: expected true got false +PASS Property anchor-scope value 'none' +PASS Property anchor-scope value 'initial' +PASS Property anchor-scope value 'all' +PASS Property anchor-scope value '--a' +PASS Property anchor-scope value '--a, --b' +PASS Property anchor-scope value '--a, --b, --c' +PASS Property anchor-scope has initial value none +PASS Property anchor-scope does not inherit diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/anchor-scope-parsing-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/anchor-scope-parsing-expected.txt index d9f375da906b3..bd1324b500889 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/anchor-scope-parsing-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/anchor-scope-parsing-expected.txt @@ -1,13 +1,13 @@ -FAIL e.style['anchor-scope'] = "initial" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['anchor-scope'] = "inherit" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['anchor-scope'] = "unset" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['anchor-scope'] = "revert" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['anchor-scope'] = "none" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['anchor-scope'] = "all" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['anchor-scope'] = "--a" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['anchor-scope'] = "--a, --b" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['anchor-scope'] = "--a, --b, --c" should set the property value assert_not_equals: property should be set got disallowed value "" +PASS e.style['anchor-scope'] = "initial" should set the property value +PASS e.style['anchor-scope'] = "inherit" should set the property value +PASS e.style['anchor-scope'] = "unset" should set the property value +PASS e.style['anchor-scope'] = "revert" should set the property value +PASS e.style['anchor-scope'] = "none" should set the property value +PASS e.style['anchor-scope'] = "all" should set the property value +PASS e.style['anchor-scope'] = "--a" should set the property value +PASS e.style['anchor-scope'] = "--a, --b" should set the property value +PASS e.style['anchor-scope'] = "--a, --b, --c" should set the property value PASS e.style['anchor-scope'] = "--a none" should not set the property value PASS e.style['anchor-scope'] = "none --a" should not set the property value PASS e.style['anchor-scope'] = "none all" should not set the property value diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt index 74ebf7b10d867..5a777b92bb4f5 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt @@ -8,6 +8,7 @@ PASS align-items PASS align-self PASS alignment-baseline PASS anchor-name +PASS anchor-scope PASS animation-composition PASS animation-delay PASS animation-direction diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/anchor-scope-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/anchor-scope-expected.txt index f03cc1c6582dc..4dce0cb773bfd 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/anchor-scope-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/anchor-scope-expected.txt @@ -1,11 +1,12 @@ -FAIL Can set 'anchor-scope' to CSS-wide keywords: initial Invalid property anchor-scope -FAIL Can set 'anchor-scope' to CSS-wide keywords: inherit Invalid property anchor-scope -FAIL Can set 'anchor-scope' to CSS-wide keywords: unset Invalid property anchor-scope -FAIL Can set 'anchor-scope' to CSS-wide keywords: revert Invalid property anchor-scope -FAIL Can set 'anchor-scope' to var() references: var(--A) Invalid property anchor-scope -FAIL Can set 'anchor-scope' to the 'none' keyword: none Invalid property anchor-scope -FAIL Can set 'anchor-scope' to the 'all' keyword: all Invalid property anchor-scope +PASS Can set 'anchor-scope' to CSS-wide keywords: initial +PASS Can set 'anchor-scope' to CSS-wide keywords: inherit +PASS Can set 'anchor-scope' to CSS-wide keywords: unset +PASS Can set 'anchor-scope' to CSS-wide keywords: revert +PASS Can set 'anchor-scope' to var() references: var(--A) +PASS Can set 'anchor-scope' to the 'none' keyword: none +PASS Can set 'anchor-scope' to the 'all' keyword: all +PASS Can set 'anchor-scope' to the '--a' keyword: --a PASS Setting 'anchor-scope' to a length: 0px throws TypeError PASS Setting 'anchor-scope' to a length: -3.14em throws TypeError PASS Setting 'anchor-scope' to a length: 3.14cm throws TypeError @@ -32,6 +33,5 @@ PASS Setting 'anchor-scope' to a number: calc(2 + 3) throws TypeError PASS Setting 'anchor-scope' to a transform: translate(50%, 50%) throws TypeError PASS Setting 'anchor-scope' to a transform: perspective(10em) throws TypeError PASS Setting 'anchor-scope' to a transform: translate3d(0px, 1px, 2px) translate(0px, 1px) rotate3d(1, 2, 3, 45deg) rotate(45deg) scale3d(1, 2, 3) scale(1, 2) skew(1deg, 1deg) skewX(1deg) skewY(45deg) perspective(1px) matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) matrix(1, 2, 3, 4, 5, 6) throws TypeError -FAIL 'anchor-scope' does not support '--a' Invalid property anchor-scope -FAIL 'anchor-scope' does not support '--a, --b' Invalid property anchor-scope +PASS 'anchor-scope' does not support '--a, --b' diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/anchor-scope.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/anchor-scope.html index 1d37b26054cbb..58ee98e3e9408 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/anchor-scope.html +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/anchor-scope.html @@ -13,10 +13,11 @@ runPropertyTests('anchor-scope', [ { syntax: 'none' }, { syntax: 'all' }, + { syntax: '--a' }, ]); runUnsupportedPropertyTests('anchor-scope', [ - '--a', '--a, --b' + '--a, --b' ]); diff --git a/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/accumulation-per-property-001-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/accumulation-per-property-001-expected.txt index 9f47abc493804..b03c71d043718 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/accumulation-per-property-001-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/accumulation-per-property-001-expected.txt @@ -12,6 +12,9 @@ PASS align-items: "flex-start" onto "flex-end" PASS align-self (type: discrete) has testAccumulation function PASS align-self: "flex-end" onto "flex-start" PASS align-self: "flex-start" onto "flex-end" +PASS anchor-scope (type: discrete) has testAccumulation function +PASS anchor-scope: "all" onto "none" +PASS anchor-scope: "none" onto "all" PASS appearance (type: discrete) has testAccumulation function PASS appearance: "none" onto "auto" PASS appearance: "auto" onto "none" diff --git a/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/addition-per-property-001-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/addition-per-property-001-expected.txt index 45aa3fdad56d4..b873288784682 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/addition-per-property-001-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/addition-per-property-001-expected.txt @@ -12,6 +12,9 @@ PASS align-items: "flex-start" onto "flex-end" PASS align-self (type: discrete) has testAddition function PASS align-self: "flex-end" onto "flex-start" PASS align-self: "flex-start" onto "flex-end" +PASS anchor-scope (type: discrete) has testAddition function +PASS anchor-scope: "all" onto "none" +PASS anchor-scope: "none" onto "all" PASS appearance (type: discrete) has testAddition function PASS appearance: "none" onto "auto" PASS appearance: "auto" onto "none" diff --git a/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-001-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-001-expected.txt index 94d31e0b1d467..de7cb25e695bd 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-001-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-001-expected.txt @@ -16,6 +16,10 @@ PASS align-self (type: discrete) has testInterpolation function PASS align-self uses discrete animation when animating between "flex-start" and "flex-end" with linear easing PASS align-self uses discrete animation when animating between "flex-start" and "flex-end" with effect easing PASS align-self uses discrete animation when animating between "flex-start" and "flex-end" with keyframe easing +PASS anchor-scope (type: discrete) has testInterpolation function +PASS anchor-scope uses discrete animation when animating between "none" and "all" with linear easing +PASS anchor-scope uses discrete animation when animating between "none" and "all" with effect easing +PASS anchor-scope uses discrete animation when animating between "none" and "all" with keyframe easing PASS appearance (type: discrete) has testInterpolation function PASS appearance uses discrete animation when animating between "auto" and "none" with linear easing PASS appearance uses discrete animation when animating between "auto" and "none" with effect easing diff --git a/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/property-list.js b/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/property-list.js index 41fc9fa5d068a..f31ee8e4ccf05 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/property-list.js +++ b/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/property-list.js @@ -25,6 +25,12 @@ const gCSSProperties1 = { { type: 'discrete', options: [ [ 'flex-start', 'flex-end' ] ] } ] }, + 'anchor-scope': { + // https://drafts.csswg.org/css-anchor-position-1/#anchor-scope + types: [ + { type: 'discrete', options: [ [ 'none', 'all' ] ] } + ] + }, 'appearance': { // https://drafts.csswg.org/css-ui/#appearance-switching types: [ diff --git a/LayoutTests/platform/glib/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt b/LayoutTests/platform/glib/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt index 4a781e829c7cd..c9140fa486625 100644 --- a/LayoutTests/platform/glib/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt +++ b/LayoutTests/platform/glib/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt @@ -8,6 +8,7 @@ PASS align-items PASS align-self PASS alignment-baseline PASS anchor-name +PASS anchor-scope PASS animation-composition PASS animation-delay PASS animation-direction diff --git a/LayoutTests/platform/ios/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt b/LayoutTests/platform/ios/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt index 0536a1d3a5a4b..eb54e0161547c 100644 --- a/LayoutTests/platform/ios/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt +++ b/LayoutTests/platform/ios/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt @@ -8,6 +8,7 @@ PASS align-items PASS align-self PASS alignment-baseline PASS anchor-name +PASS anchor-scope PASS animation-composition PASS animation-delay PASS animation-direction diff --git a/LayoutTests/platform/ipad/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt b/LayoutTests/platform/ipad/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt index 5a892636fbc2e..a2c0758283744 100644 --- a/LayoutTests/platform/ipad/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt +++ b/LayoutTests/platform/ipad/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt @@ -8,6 +8,7 @@ PASS align-items PASS align-self PASS alignment-baseline PASS anchor-name +PASS anchor-scope PASS animation-composition PASS animation-delay PASS animation-direction diff --git a/LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt b/LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt index 7faec1d6de09c..8a0b59818c9b7 100644 --- a/LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt +++ b/LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt @@ -8,6 +8,7 @@ PASS align-items PASS align-self PASS alignment-baseline PASS anchor-name +PASS anchor-scope PASS animation-composition PASS animation-delay PASS animation-direction diff --git a/Source/WebCore/Headers.cmake b/Source/WebCore/Headers.cmake index 902dca60b9717..b587108cceb32 100644 --- a/Source/WebCore/Headers.cmake +++ b/Source/WebCore/Headers.cmake @@ -940,7 +940,6 @@ set(WebCore_PRIVATE_FRAMEWORK_HEADERS css/MediaList.h css/MediaQueryParserContext.h css/MutableStyleProperties.h - css/NameScope.h css/Quad.h css/Rect.h css/RectBase.h @@ -2615,6 +2614,7 @@ set(WebCore_PRIVATE_FRAMEWORK_HEADERS rendering/style/GridTrackSize.h rendering/style/LineClampValue.h rendering/style/ListStyleType.h + rendering/style/NameScope.h rendering/style/NinePieceImage.h rendering/style/OffsetRotation.h rendering/style/OutlineValue.h diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj index 825269af0e9b4..57711ce80a0f7 100644 --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj @@ -8015,7 +8015,7 @@ 1AAADDE114DC8C8F00AF64B3 /* ScrollingTreeNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollingTreeNode.cpp; sourceTree = ""; }; 1AAADDE214DC8C8F00AF64B3 /* ScrollingTreeNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollingTreeNode.h; sourceTree = ""; }; 1AB33DA412551E320024457A /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; - 1AB39CC62C98CEA100B8AD82 /* NameScope.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NameScope.h; sourceTree = ""; }; + 1AB39CC62C98CEA100B8AD82 /* NameScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NameScope.h; sourceTree = ""; }; 1AB40EDF1BF4271E00BA81BE /* ContextMenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContextMenu.cpp; sourceTree = ""; }; 1AB40EE01BF4271E00BA81BE /* ContextMenuItem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContextMenuItem.cpp; sourceTree = ""; }; 1AB5EBCF194A1D170059AC70 /* ShapeValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShapeValue.cpp; sourceTree = ""; }; @@ -34778,6 +34778,7 @@ AB31C91D10AE1B8E000C7B92 /* LineClampValue.h */, 4CF2530E29C09454005CDDAA /* ListStyleType.cpp */, 4C96385729BF4A51003E5741 /* ListStyleType.h */, + 1AB39CC62C98CEA100B8AD82 /* NameScope.h */, BCEF43DF0E674110001C1287 /* NinePieceImage.cpp */, BCEF43DC0E674012001C1287 /* NinePieceImage.h */, 668A1B032723A41500765E0F /* OffsetRotation.cpp */, @@ -37988,7 +37989,6 @@ 4471710C205AF945000A116E /* MediaQueryParserContext.h */, 930A76C7297FA1C40055B743 /* MutableStyleProperties.cpp */, 930A76C6297FA1C30055B743 /* MutableStyleProperties.h */, - 1AB39CC62C98CEA100B8AD82 /* NameScope.h */, 49CCE6F72997441F006C91EB /* popover.css */, BC8A133629177FB700096A9F /* process-css-properties.py */, 491342972B425C0200FEEF18 /* process-css-pseudo-selectors.py */, diff --git a/Source/WebCore/animation/CSSPropertyAnimation.cpp b/Source/WebCore/animation/CSSPropertyAnimation.cpp index f33d5e88f59c4..1ad5824244bba 100644 --- a/Source/WebCore/animation/CSSPropertyAnimation.cpp +++ b/Source/WebCore/animation/CSSPropertyAnimation.cpp @@ -4160,6 +4160,7 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new DiscretePropertyWrapper(CSSPropertyViewTransitionName, &RenderStyle::viewTransitionName, &RenderStyle::setViewTransitionName), new DiscretePropertyWrapper(CSSPropertyFieldSizing, &RenderStyle::fieldSizing, &RenderStyle::setFieldSizing), new DiscretePropertyWrapper&>(CSSPropertyAnchorName, &RenderStyle::anchorNames, &RenderStyle::setAnchorNames), + new DiscretePropertyWrapper(CSSPropertyAnchorScope, &RenderStyle::anchorScope, &RenderStyle::setAnchorScope), new DiscretePropertyWrapper&>(CSSPropertyPositionAnchor, &RenderStyle::positionAnchor, &RenderStyle::setPositionAnchor), new DiscretePropertyWrapper>(CSSPropertyPositionArea, &RenderStyle::positionArea, &RenderStyle::setPositionArea), new DiscretePropertyWrapper(CSSPropertyPositionTryOrder, &RenderStyle::positionTryOrder, &RenderStyle::setPositionTryOrder), diff --git a/Source/WebCore/css/CSSProperties.json b/Source/WebCore/css/CSSProperties.json index 94a576bc961f7..bcaf1a2af39b6 100644 --- a/Source/WebCore/css/CSSProperties.json +++ b/Source/WebCore/css/CSSProperties.json @@ -11358,6 +11358,19 @@ "url": "https://drafts.csswg.org/css-anchor-position-1/#name" } }, + "anchor-scope": { + "animation-type": "discrete", + "initial": "none", + "codegen-properties": { + "style-builder-converter": "NameScope", + "parser-grammar": "none | all | #", + "settings-flag": "cssAnchorPositioningEnabled" + }, + "specification": { + "category": "css-anchor-position", + "url": "https://drafts.csswg.org/css-anchor-position-1/#anchor-scope" + } + }, "position-anchor": { "animation-type": "discrete", "initial": "auto", diff --git a/Source/WebCore/css/ComputedStyleExtractor.cpp b/Source/WebCore/css/ComputedStyleExtractor.cpp index c28268a2bca4b..0b1b1ef635708 100644 --- a/Source/WebCore/css/ComputedStyleExtractor.cpp +++ b/Source/WebCore/css/ComputedStyleExtractor.cpp @@ -3298,17 +3298,30 @@ static Ref valueForPositionArea(const std::optional& pos return CSSPropertyParserHelpers::valueForPositionArea(blockOrXAxisKeyword, inlineOrYAxisKeyword).releaseNonNull(); } -static Ref valueForNameScopeNames(const Vector& names) +static Ref valueForNameScope(const NameScope& scope) { - if (names.isEmpty()) + switch (scope.type) { + case NameScope::Type::None: return CSSPrimitiveValue::create(CSSValueNone); - CSSValueListBuilder list; - for (auto& name : names) { - ASSERT(!name.isNull()); - list.append(CSSPrimitiveValue::createCustomIdent(name)); + case NameScope::Type::All: + return CSSPrimitiveValue::create(CSSValueAll); + + case NameScope::Type::Ident: + if (scope.names.isEmpty()) + return CSSPrimitiveValue::create(CSSValueNone); + + CSSValueListBuilder list; + for (auto& name : scope.names) { + ASSERT(!name.isNull()); + list.append(CSSPrimitiveValue::createCustomIdent(name)); + } + + return CSSValueList::createCommaSeparated(WTFMove(list)); } - return CSSValueList::createCommaSeparated(WTFMove(list)); + + ASSERT_NOT_REACHED(); + return CSSPrimitiveValue::create(CSSValueNone); } static Ref scrollTimelineShorthandValue(const Vector>& timelines) @@ -4979,6 +4992,8 @@ RefPtr ComputedStyleExtractor::valueForPropertyInStyle(const RenderSty case CSSPropertyAnchorName: return valueForAnchorName(style.anchorNames()); + case CSSPropertyAnchorScope: + return valueForNameScope(style.anchorScope()); case CSSPropertyPositionAnchor: if (!style.positionAnchor()) return CSSPrimitiveValue::create(CSSValueAuto); @@ -5004,16 +5019,7 @@ RefPtr ComputedStyleExtractor::valueForPropertyInStyle(const RenderSty return CSSPrimitiveValue::create(CSSValueNormal); } case CSSPropertyTimelineScope: - switch (style.timelineScope().type) { - case NameScope::Type::None: - return CSSPrimitiveValue::create(CSSValueNone); - case NameScope::Type::All: - return CSSPrimitiveValue::create(CSSValueAll); - case NameScope::Type::Ident: - return valueForNameScopeNames(style.timelineScope().names); - } - ASSERT_NOT_REACHED(); - return CSSPrimitiveValue::create(CSSValueNone); + return valueForNameScope(style.timelineScope()); // Unimplemented CSS 3 properties (including CSS3 shorthand properties). case CSSPropertyAll: diff --git a/Source/WebCore/css/NameScope.h b/Source/WebCore/rendering/style/NameScope.h similarity index 100% rename from Source/WebCore/css/NameScope.h rename to Source/WebCore/rendering/style/NameScope.h diff --git a/Source/WebCore/rendering/style/RenderStyle.cpp b/Source/WebCore/rendering/style/RenderStyle.cpp index 12d4245fdf091..ee6e1cfefaa91 100644 --- a/Source/WebCore/rendering/style/RenderStyle.cpp +++ b/Source/WebCore/rendering/style/RenderStyle.cpp @@ -1987,6 +1987,8 @@ void RenderStyle::conservativelyCollectChangedAnimatableProperties(const RenderS changingProperties.m_properties.set(CSSPropertyContentVisibility); if (first.anchorNames != second.anchorNames) changingProperties.m_properties.set(CSSPropertyAnchorName); + if (first.anchorScope != second.anchorScope) + changingProperties.m_properties.set(CSSPropertyAnchorScope); if (first.positionAnchor != second.positionAnchor) changingProperties.m_properties.set(CSSPropertyPositionAnchor); if (first.positionArea != second.positionArea) diff --git a/Source/WebCore/rendering/style/RenderStyle.h b/Source/WebCore/rendering/style/RenderStyle.h index be54c8a633c5c..386afc2254e11 100644 --- a/Source/WebCore/rendering/style/RenderStyle.h +++ b/Source/WebCore/rendering/style/RenderStyle.h @@ -2320,6 +2320,10 @@ class RenderStyle final : public CanMakeCheckedPtr { inline const Vector& anchorNames() const; inline void setAnchorNames(const Vector&); + static inline NameScope initialAnchorScope(); + inline const NameScope& anchorScope() const; + inline void setAnchorScope(const NameScope&); + static inline std::optional initialPositionAnchor(); inline const std::optional& positionAnchor() const; inline void setPositionAnchor(const std::optional&); diff --git a/Source/WebCore/rendering/style/RenderStyleInlines.h b/Source/WebCore/rendering/style/RenderStyleInlines.h index 649bde025a714..3af5b324650b3 100644 --- a/Source/WebCore/rendering/style/RenderStyleInlines.h +++ b/Source/WebCore/rendering/style/RenderStyleInlines.h @@ -82,6 +82,7 @@ constexpr auto RenderStyle::allTransformOperations() -> OptionSetmiscData->animations.get(); } inline AnimationList* RenderStyle::animations() { return m_nonInheritedData->miscData->animations.get(); } inline const Vector& RenderStyle::anchorNames() const { return m_nonInheritedData->rareData->anchorNames; } +inline const NameScope& RenderStyle::anchorScope() const { return m_nonInheritedData->rareData->anchorScope; } inline StyleAppearance RenderStyle::appearance() const { return static_cast(m_nonInheritedData->miscData->appearance); } inline const FilterOperations& RenderStyle::appleColorFilter() const { return m_rareInheritedData->appleColorFilter->operations; } #if HAVE(CORE_MATERIAL) @@ -350,6 +351,7 @@ inline const NamedGridLinesMap& RenderStyle::implicitNamedGridRowLines() const { constexpr auto RenderStyle::individualTransformOperations() -> OptionSet { return { TransformOperationOption::Translate, TransformOperationOption::Rotate, TransformOperationOption::Scale, TransformOperationOption::Offset }; } inline const StyleCustomPropertyData& RenderStyle::inheritedCustomProperties() const { return m_rareInheritedData->customProperties.get(); } inline Vector RenderStyle::initialAnchorNames() { return { }; } +inline NameScope RenderStyle::initialAnchorScope() { return { }; } constexpr StyleAppearance RenderStyle::initialAppearance() { return StyleAppearance::None; } #if HAVE(CORE_MATERIAL) constexpr AppleVisualEffect RenderStyle::initialAppleVisualEffect() { return AppleVisualEffect::None; } diff --git a/Source/WebCore/rendering/style/RenderStyleSetters.h b/Source/WebCore/rendering/style/RenderStyleSetters.h index 4351c61d764b6..938fc9698c700 100644 --- a/Source/WebCore/rendering/style/RenderStyleSetters.h +++ b/Source/WebCore/rendering/style/RenderStyleSetters.h @@ -78,6 +78,7 @@ inline void RenderStyle::setAlignItemsPosition(ItemPosition position) { m_nonInh inline void RenderStyle::setAlignSelf(const StyleSelfAlignmentData& data) { SET_NESTED(m_nonInheritedData, miscData, alignSelf, data); } inline void RenderStyle::setAlignSelfPosition(ItemPosition position) { m_nonInheritedData.access().miscData.access().alignSelf.setPosition(position); } inline void RenderStyle::setAnchorNames(const Vector& names) { SET_NESTED(m_nonInheritedData, rareData, anchorNames, names); } +inline void RenderStyle::setAnchorScope(const NameScope& scope) { SET_NESTED(m_nonInheritedData, rareData, anchorScope, scope); } inline void RenderStyle::setAppearance(StyleAppearance appearance) { SET_NESTED_PAIR(m_nonInheritedData, miscData, appearance, static_cast(appearance), usedAppearance, static_cast(appearance)); } inline void RenderStyle::setAppleColorFilter(FilterOperations&& ops) { SET_NESTED(m_rareInheritedData, appleColorFilter, operations, WTFMove(ops)); } #if HAVE(CORE_MATERIAL) diff --git a/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp b/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp index 6d016cee42a0d..9f603d69fc2b9 100644 --- a/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp +++ b/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp @@ -98,6 +98,7 @@ StyleRareNonInheritedData::StyleRareNonInheritedData() // scrollSnapStop , pseudoElementNameArgument(nullAtom()) , anchorNames(RenderStyle::initialAnchorNames()) + , anchorScope(RenderStyle::initialAnchorScope()) , positionAnchor(RenderStyle::initialPositionAnchor()) , positionArea(RenderStyle::initialPositionArea()) , positionTryFallbacks(RenderStyle::initialPositionTryFallbacks()) @@ -203,6 +204,7 @@ inline StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonIn , scrollSnapStop(o.scrollSnapStop) , pseudoElementNameArgument(o.pseudoElementNameArgument) , anchorNames(o.anchorNames) + , anchorScope(o.anchorScope) , positionAnchor(o.positionAnchor) , positionArea(o.positionArea) , positionTryFallbacks(o.positionTryFallbacks) @@ -313,6 +315,7 @@ bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) c && scrollSnapStop == o.scrollSnapStop && pseudoElementNameArgument == o.pseudoElementNameArgument && anchorNames == o.anchorNames + && anchorScope == o.anchorScope && positionAnchor == o.positionAnchor && positionArea == o.positionArea && positionTryFallbacks == o.positionTryFallbacks @@ -472,6 +475,7 @@ void StyleRareNonInheritedData::dumpDifferences(TextStream& ts, const StyleRareN LOG_IF_DIFFERENT(pseudoElementNameArgument); LOG_IF_DIFFERENT(anchorNames); + LOG_IF_DIFFERENT(anchorScope); LOG_IF_DIFFERENT(positionAnchor); LOG_IF_DIFFERENT(positionArea); LOG_IF_DIFFERENT(positionTryFallbacks); diff --git a/Source/WebCore/rendering/style/StyleRareNonInheritedData.h b/Source/WebCore/rendering/style/StyleRareNonInheritedData.h index 5619da92107be..d9969fbef4169 100644 --- a/Source/WebCore/rendering/style/StyleRareNonInheritedData.h +++ b/Source/WebCore/rendering/style/StyleRareNonInheritedData.h @@ -219,6 +219,7 @@ class StyleRareNonInheritedData : public RefCounted { AtomString pseudoElementNameArgument; Vector anchorNames; + NameScope anchorScope; std::optional positionAnchor; std::optional positionArea; Vector positionTryFallbacks; From 894f7621ad561152153a588951cbed7f6069769e Mon Sep 17 00:00:00 2001 From: Ben Nham Date: Sun, 23 Feb 2025 10:25:05 -0800 Subject: [PATCH 089/174] [Site Isolation] Propagate mute and volume state to remote frames https://bugs.webkit.org/show_bug.cgi?id=288136 rdar://145241258 Reviewed by Eric Carlson. There are a number of media-related WebPageProxy methods which only forward state changes to the main frame and not to out of process iframes. In this patch we forward mute and volume state changes to OOP iframes. Previously we were only propagating those state changes to the main frame's process. * Source/WebCore/testing/Internals.cpp: (WebCore::Internals::pageMediaVolume): * Source/WebCore/testing/Internals.h: * Source/WebCore/testing/Internals.idl: * Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h: * Source/WebKit/UIProcess/API/Cocoa/WKWebViewTesting.mm: (-[WKWebView _setMediaVolumeForTesting:]): * Source/WebKit/UIProcess/WebPageProxy.cpp: (WebKit::WebPageProxy::setMediaVolume): (WebKit::WebPageProxy::setMuted): * Tools/TestWebKitAPI/Tests/WebKitCocoa/SiteIsolation.mm: (TestWebKitAPI::TEST(SiteIsolation, MutesAndSetsAudioInMultipleFrames)): Canonical link: https://commits.webkit.org/290910@main --- Source/WebCore/testing/Internals.cpp | 13 +++ Source/WebCore/testing/Internals.h | 1 + Source/WebCore/testing/Internals.idl | 2 + .../API/Cocoa/WKWebViewPrivateForTesting.h | 3 + .../UIProcess/API/Cocoa/WKWebViewTesting.mm | 5 ++ Source/WebKit/UIProcess/WebPageProxy.cpp | 16 +++- .../Tests/WebKitCocoa/SiteIsolation.mm | 80 +++++++++++++++++++ 7 files changed, 116 insertions(+), 4 deletions(-) diff --git a/Source/WebCore/testing/Internals.cpp b/Source/WebCore/testing/Internals.cpp index 1b1a75f13f577..4e124bca01716 100644 --- a/Source/WebCore/testing/Internals.cpp +++ b/Source/WebCore/testing/Internals.cpp @@ -5695,6 +5695,19 @@ void Internals::setPageMediaVolume(float volume) page->setMediaVolume(volume); } +float Internals::pageMediaVolume() +{ + Document* document = contextDocument(); + if (!document) + return 0; + + Page* page = document->page(); + if (!page) + return 0; + + return page->mediaVolume(); +} + #if !PLATFORM(COCOA) String Internals::userVisibleString(const DOMURL& url) diff --git a/Source/WebCore/testing/Internals.h b/Source/WebCore/testing/Internals.h index a15b7f0a0df1b..3024287ac35a9 100644 --- a/Source/WebCore/testing/Internals.h +++ b/Source/WebCore/testing/Internals.h @@ -922,6 +922,7 @@ class Internals final void setMediaControlsHidePlaybackRates(HTMLMediaElement&, bool); #endif // ENABLE(VIDEO) + float pageMediaVolume(); void setPageMediaVolume(float); String userVisibleString(const DOMURL&); diff --git a/Source/WebCore/testing/Internals.idl b/Source/WebCore/testing/Internals.idl index 25a2c747cbabb..d794fee8b1208 100644 --- a/Source/WebCore/testing/Internals.idl +++ b/Source/WebCore/testing/Internals.idl @@ -1087,6 +1087,8 @@ enum ContentsFormat { [Conditional=VIDEO] undefined setMediaControlsHidePlaybackRates(HTMLMediaElement element, boolean hidePlaybackRates); DOMString userVisibleString(DOMURL url); + + float pageMediaVolume(); undefined setPageMediaVolume(float volume); undefined setShowAllPlugins(boolean showAll); diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h b/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h index 98bc35dc2525e..2e57c44fef217 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h @@ -150,6 +150,9 @@ struct WKAppPrivacyReportTestingData { - (void)_getNotifyStateForTesting:(NSString *)notificationName completionHandler:(void(^)(NSNumber * _Nullable))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA), visionos(WK_XROS_TBA)); @property (nonatomic, readonly) BOOL _hasAccessibilityActivityForTesting; + +- (void)_setMediaVolumeForTesting:(float)volume; + @end typedef NS_ENUM(NSInteger, _WKMediaSessionReadyState) { diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebViewTesting.mm b/Source/WebKit/UIProcess/API/Cocoa/WKWebViewTesting.mm index c545886018713..cadff2d39d8fb 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebViewTesting.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebViewTesting.mm @@ -916,6 +916,11 @@ - (BOOL)_hasAccessibilityActivityForTesting #endif } +- (void)_setMediaVolumeForTesting:(float)mediaVolume +{ + _page->setMediaVolume(mediaVolume); +} + @end #if ENABLE(MEDIA_SESSION_COORDINATOR) diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp index d0f4cb5248462..5eb9439d6b735 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.cpp +++ b/Source/WebKit/UIProcess/WebPageProxy.cpp @@ -9006,8 +9006,10 @@ void WebPageProxy::setMediaVolume(float volume) if (!hasRunningProcess()) return; - - send(Messages::WebPage::SetMediaVolume(volume)); + + forEachWebContentProcess([&](auto& webProcess, auto pageID) { + webProcess.send(Messages::WebPage::SetMediaVolume(volume), pageID); + }); } #if ENABLE(MEDIA_STREAM) @@ -9077,7 +9079,9 @@ void WebPageProxy::setMuted(WebCore::MediaProducerMutedStateFlags state, FromApp WebProcessProxy::muteCaptureInPagesExcept(m_webPageID); #endif // ENABLE(MEDIA_STREAM) - protectedLegacyMainFrameProcess()->pageMutedStateChanged(m_webPageID, state); + forEachWebContentProcess([&] (auto& process, auto pageID) { + process.pageMutedStateChanged(pageID, state); + }); #if ENABLE(MEDIA_STREAM) auto newState = applyWebAppDesiredMutedKinds(state, m_mutedCaptureKindsDesiredByWebApp); @@ -9086,7 +9090,11 @@ void WebPageProxy::setMuted(WebCore::MediaProducerMutedStateFlags state, FromApp #endif WEBPAGEPROXY_RELEASE_LOG(Media, "setMuted, app state = %d, final state = %d", state.toRaw(), newState.toRaw()); - sendWithAsyncReply(Messages::WebPage::SetMuted(newState), WTFMove(completionHandler)); + auto aggregator = CallbackAggregator::create(WTFMove(completionHandler)); + forEachWebContentProcess([&](auto& webProcess, auto pageID) { + webProcess.sendWithAsyncReply(Messages::WebPage::SetMuted(newState), [aggregator] { }, pageID); + }); + activityStateDidChange({ ActivityState::IsAudible, ActivityState::IsCapturingMedia }); } diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/SiteIsolation.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/SiteIsolation.mm index 0ba88a9e3eee6..7e434285b0e40 100644 --- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/SiteIsolation.mm +++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/SiteIsolation.mm @@ -4000,6 +4000,86 @@ HTTPServer server({ expectPlayingAudio(webView.get(), false, "Should not be playing audio after removing iframe"_s); } +TEST(SiteIsolation, MutesAndSetsAudioInMultipleFrames) +{ + auto mainFrameHTML = "" + ""_s; + auto subFrameHTML = ""_s; + + RetainPtr videoData = [NSData dataWithContentsOfFile:[NSBundle.test_resourcesBundle pathForResource:@"video-with-audio" ofType:@"mp4"] options:0 error:NULL]; + HTTPResponse videoResponse { videoData.get() }; + videoResponse.headerFields.set("Content-Type"_s, "video/mp4"_s); + + HTTPServer server({ + { "/mainframe"_s, { { { "Content-Type"_s, "text/html"_s } }, mainFrameHTML } }, + { "/subframe"_s, { { { "Content-Type"_s, "text/html"_s } }, subFrameHTML } }, + { "/video-with-audio.mp4"_s, { videoData.get() } }, + }, HTTPServer::Protocol::HttpsProxy); + + WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES]; + auto storeConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration]); + [storeConfiguration setHTTPSProxy:[NSURL URLWithString:[NSString stringWithFormat:@"https://127.0.0.1:%d/", server.port()]]]; + [configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:storeConfiguration.get()]).get()]; + enableSiteIsolation(configuration); + + auto [webView, navigationDelegate] = siteIsolatedViewAndDelegate(configuration); + [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://example.com/mainframe"]]]; + [navigationDelegate waitForDidFinishNavigation]; + + callMethodOnFirstVideoElementInFrame(webView.get(), @"play", nil); + expectPlayingAudio(webView.get(), true, "Should be playing audio in main frame"_s); + + callMethodOnFirstVideoElementInFrame(webView.get(), @"play", [webView firstChildFrame]); + expectPlayingAudio(webView.get(), true, "Should be playing audio in remote frame"_s); + + auto expectMuted = [&](bool expectedMuted, WKFrameInfo *frame, ASCIILiteral reason) { + bool success = TestWebKitAPI::Util::waitFor([&]() { + id actuallyMuted = [webView objectByEvaluatingJavaScript:@"window.internals.isEffectivelyMuted(document.getElementsByTagName('video')[0])" inFrame:frame]; + return [actuallyMuted boolValue] == expectedMuted; + }); + EXPECT_TRUE(success) << reason.characters(); + }; + + auto expectMediaVolume = [&](float expectedMediaVolume, WKFrameInfo *frame, ASCIILiteral reason) { + bool success = TestWebKitAPI::Util::waitFor([&]() { + id actualMediaVolume = [webView objectByEvaluatingJavaScript:@"window.internals.pageMediaVolume()" inFrame:frame]; + return [actualMediaVolume floatValue] == expectedMediaVolume; + }); + EXPECT_TRUE(success) << reason.characters(); + }; + + expectMuted(false, nil, "Should not be muted in main frame"_s); + expectMuted(false, [webView firstChildFrame], "Should not be muted in remote frame"_s); + + [webView _setPageMuted:_WKMediaAudioMuted]; + [webView _setMediaVolumeForTesting:0.125f]; + + expectMuted(true, nil, "Should be muted in main frame"_s); + expectMediaVolume(0.125f, nil, "Should set volume in main frame"_s); + expectMuted(true, [webView firstChildFrame], "Should be muted in remote frame"_s); + expectMediaVolume(0.125f, nil, "Should set volume in remote frame"_s); + + auto addFrameToBody = @"" + "return new Promise((resolve, reject) => {" + " let frame = document.createElement('iframe');" + " frame.onload = () => resolve(true);" + " frame.setAttribute('src', 'https://webkit.org/subframe');" + " document.body.appendChild(frame);" + "})"; + __block RetainPtr error; + __block bool done = false; + [webView callAsyncJavaScript:addFrameToBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id result, NSError *callError) { + error = callError; + done = true; + }]; + Util::run(&done); + EXPECT_FALSE(!!error) << "Failed to add iframe: " << [error description].UTF8String; + + callMethodOnFirstVideoElementInFrame(webView.get(), @"play", [webView secondChildFrame]); + expectMuted(true, [webView secondChildFrame], "Should be muted in newly created remote frame"_s); + expectMediaVolume(0.125f, [webView secondChildFrame], "Should initialize newly created remote frame with previously set media volume"_s); +} + TEST(SiteIsolation, FrameServerTrust) { HTTPServer plaintextServer({ From f1e6410c9daa6a234346914625923b8d3b2f04fa Mon Sep 17 00:00:00 2001 From: Antti Koivisto Date: Sun, 23 Feb 2025 10:35:04 -0800 Subject: [PATCH 090/174] [css-anchor-position-1] Parse @position-try name in position-try-fallback https://bugs.webkit.org/show_bug.cgi?id=288311 rdar://problem/145419344 Reviewed by Alan Baradlay. Parse part in position-try-fallback syntax none | [ [ || ] | <'position-area'> ]# https://drafts.csswg.org/css-anchor-position-1/#position-try-fallbacks * LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/position-try-fallbacks-computed-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/position-try-fallbacks-parsing-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/position-area-in-position-try-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/property-interpolations-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/try-tactic-basic-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/try-tactic-margin-expected.txt: * Source/WebCore/css/ComputedStyleExtractor.cpp: (WebCore::valueForScopedName): (WebCore::valueForPositionTryFallbacks): Serialize. * Source/WebCore/css/parser/CSSPropertyParserConsumer+PositionTry.cpp: (WebCore::CSSPropertyParserHelpers::consumePositionTryFallbacks): Parse. * Source/WebCore/rendering/style/PositionTryFallback.h: Add positionTryRuleName field. * Source/WebCore/style/StyleBuilderConverter.h: (WebCore::Style::BuilderConverter::convertPositionTryFallbacks): Build style. * Source/WebCore/style/StyleTreeResolver.cpp: (WebCore::Style::TreeResolver::tryChoosePositionOption): Fix to prevent eternal loop in css/css-anchor-position/chrome-40286059-crash.html Canonical link: https://commits.webkit.org/290911@main --- ...sition-try-fallbacks-computed-expected.txt | 6 +- ...osition-try-fallbacks-parsing-expected.txt | 14 +- ...position-area-in-position-try-expected.txt | 118 ++++---- .../property-interpolations-expected.txt | 252 +++++++++--------- .../try-tactic-basic-expected.txt | 32 +-- .../try-tactic-margin-expected.txt | 16 +- Source/WebCore/css/ComputedStyleExtractor.cpp | 22 +- .../CSSPropertyParserConsumer+PositionTry.cpp | 13 + .../rendering/style/PositionTryFallback.h | 3 + Source/WebCore/style/StyleBuilderConverter.h | 15 +- Source/WebCore/style/StyleTreeResolver.cpp | 8 +- 11 files changed, 262 insertions(+), 237 deletions(-) diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/position-try-fallbacks-computed-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/position-try-fallbacks-computed-expected.txt index 73586e825be5e..cfe11d698c412 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/position-try-fallbacks-computed-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/position-try-fallbacks-computed-expected.txt @@ -4,10 +4,10 @@ PASS Property position-try-fallbacks value 'flip-block' PASS Property position-try-fallbacks value 'flip-inline' PASS Property position-try-fallbacks value 'flip-start' PASS Property position-try-fallbacks value 'flip-block, flip-inline' -FAIL Property position-try-fallbacks value '--foo, --bar' assert_true: '--foo, --bar' is a supported value for position-try-fallbacks. expected true got false +PASS Property position-try-fallbacks value '--foo, --bar' PASS Property position-try-fallbacks value 'flip-start flip-inline flip-block' -FAIL Property position-try-fallbacks value 'flip-start --flop' assert_true: 'flip-start --flop' is a supported value for position-try-fallbacks. expected true got false -FAIL Property position-try-fallbacks value '--flop flip-start' assert_true: '--flop flip-start' is a supported value for position-try-fallbacks. expected true got false +PASS Property position-try-fallbacks value 'flip-start --flop' +PASS Property position-try-fallbacks value '--flop flip-start' FAIL Property position-try-fallbacks value 'left top' assert_true: 'left top' is a supported value for position-try-fallbacks. expected true got false FAIL Property position-try-fallbacks value 'top left' assert_true: 'top left' is a supported value for position-try-fallbacks. expected true got false FAIL Property position-try-fallbacks value 'start start' assert_true: 'start start' is a supported value for position-try-fallbacks. expected true got false diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/position-try-fallbacks-parsing-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/position-try-fallbacks-parsing-expected.txt index 8eb46c1f4e36b..abb3923077173 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/position-try-fallbacks-parsing-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/parsing/position-try-fallbacks-parsing-expected.txt @@ -10,13 +10,13 @@ PASS e.style['position-try-fallbacks'] = "flip-start, flip-block" should set the PASS e.style['position-try-fallbacks'] = "flip-start flip-inline, flip-block" should set the property value PASS e.style['position-try-fallbacks'] = "flip-start, flip-start" should set the property value PASS e.style['position-try-fallbacks'] = "flip-start flip-inline flip-block" should set the property value -FAIL e.style['position-try-fallbacks'] = "flip-block, --foo" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['position-try-fallbacks'] = "--bar, flip-block flip-start" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['position-try-fallbacks'] = "--foo, --bar, --baz" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['position-try-fallbacks'] = "--bar flip-block" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['position-try-fallbacks'] = "--bar flip-inline flip-block" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['position-try-fallbacks'] = "flip-inline --foo" should set the property value assert_not_equals: property should be set got disallowed value "" -FAIL e.style['position-try-fallbacks'] = "flip-inline flip-start --foo" should set the property value assert_not_equals: property should be set got disallowed value "" +PASS e.style['position-try-fallbacks'] = "flip-block, --foo" should set the property value +PASS e.style['position-try-fallbacks'] = "--bar, flip-block flip-start" should set the property value +PASS e.style['position-try-fallbacks'] = "--foo, --bar, --baz" should set the property value +PASS e.style['position-try-fallbacks'] = "--bar flip-block" should set the property value +PASS e.style['position-try-fallbacks'] = "--bar flip-inline flip-block" should set the property value +PASS e.style['position-try-fallbacks'] = "flip-inline --foo" should set the property value +PASS e.style['position-try-fallbacks'] = "flip-inline flip-start --foo" should set the property value FAIL e.style['position-try-fallbacks'] = "left top" should set the property value assert_not_equals: property should be set got disallowed value "" FAIL e.style['position-try-fallbacks'] = "top left" should set the property value assert_not_equals: property should be set got disallowed value "" FAIL e.style['position-try-fallbacks'] = "start start" should set the property value assert_not_equals: property should be set got disallowed value "" diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/position-area-in-position-try-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/position-area-in-position-try-expected.txt index 19ce9637ddef8..1fc9535fd609c 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/position-area-in-position-try-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/position-area-in-position-try-expected.txt @@ -1,61 +1,61 @@ -FAIL none assert_true: expected true got false -FAIL span-all assert_true: expected true got false -FAIL span-all span-all assert_true: expected true got false -FAIL top left assert_true: expected true got false -FAIL top center assert_true: expected true got false -FAIL top right assert_true: expected true got false -FAIL center left assert_true: expected true got false -FAIL center center assert_true: expected true got false -FAIL center right assert_true: expected true got false -FAIL bottom left assert_true: expected true got false -FAIL bottom center assert_true: expected true got false -FAIL bottom right assert_true: expected true got false -FAIL start start assert_true: expected true got false -FAIL start center assert_true: expected true got false -FAIL start end assert_true: expected true got false -FAIL center start assert_true: expected true got false -FAIL center end assert_true: expected true got false -FAIL end start assert_true: expected true got false -FAIL end center assert_true: expected true got false -FAIL end end assert_true: expected true got false -FAIL self-start self-start assert_true: expected true got false -FAIL self-start center assert_true: expected true got false -FAIL self-start self-end assert_true: expected true got false -FAIL center self-start assert_true: expected true got false -FAIL center self-end assert_true: expected true got false -FAIL self-end self-start assert_true: expected true got false -FAIL self-end center assert_true: expected true got false -FAIL self-end self-end assert_true: expected true got false -FAIL y-start x-start assert_true: expected true got false -FAIL y-start center assert_true: expected true got false -FAIL y-start x-end assert_true: expected true got false -FAIL center x-start assert_true: expected true got false -FAIL center x-end assert_true: expected true got false -FAIL y-end x-start assert_true: expected true got false -FAIL y-end center assert_true: expected true got false -FAIL y-end x-end assert_true: expected true got false -FAIL y-self-start x-self-start assert_true: expected true got false -FAIL y-self-start center assert_true: expected true got false -FAIL y-self-start x-self-end assert_true: expected true got false -FAIL center x-self-start assert_true: expected true got false -FAIL center x-self-end assert_true: expected true got false -FAIL y-self-end x-self-start assert_true: expected true got false -FAIL y-self-end center assert_true: expected true got false -FAIL y-self-end x-self-end assert_true: expected true got false -FAIL span-y-self-start span-x-self-end assert_true: expected true got false -FAIL span-bottom span-all assert_true: expected true got false -FAIL Placement: --top assert_true: expected true got false -FAIL Placement: --left assert_true: expected true got false -FAIL Placement: --right, --top assert_true: expected true got false -FAIL Placement: --bottom, --top assert_true: expected true got false -FAIL Placement: --bottom, --right, --top assert_true: expected true got false -FAIL Placement: --bottom, --right, --left, --top assert_true: expected true got false -FAIL Placement: --bottom, --left, --top, --right assert_true: expected true got false -FAIL Placement: --right flip-inline assert_true: expected true got false -FAIL Placement: --bottom flip-block assert_true: expected true got false -FAIL Placement: --left flip-start assert_true: expected true got false -FAIL Placement: --left flip-inline, --top assert_true: expected true got false -FAIL Placement: --top flip-block, --left assert_true: expected true got false -FAIL Placement: --left flip-start flip-block, --left assert_true: expected true got false +FAIL none assert_equals: offsetLeft expected 0 but got 200 +FAIL span-all assert_equals: offsetLeft expected 0 but got 200 +FAIL span-all span-all assert_equals: offsetLeft expected 0 but got 200 +FAIL top left assert_equals: offsetLeft expected 0 but got 200 +FAIL top center assert_equals: offsetLeft expected 0 but got 200 +FAIL top right assert_equals: offsetLeft expected 0 but got 200 +FAIL center left assert_equals: offsetLeft expected 0 but got 200 +FAIL center center assert_equals: offsetLeft expected 0 but got 200 +FAIL center right assert_equals: offsetLeft expected 0 but got 200 +FAIL bottom left assert_equals: offsetLeft expected 0 but got 200 +FAIL bottom center assert_equals: offsetLeft expected 0 but got 200 +FAIL bottom right assert_equals: offsetLeft expected 0 but got 200 +FAIL start start assert_equals: offsetLeft expected 0 but got 200 +FAIL start center assert_equals: offsetLeft expected 0 but got 200 +FAIL start end assert_equals: offsetLeft expected 0 but got 200 +FAIL center start assert_equals: offsetLeft expected 0 but got 200 +FAIL center end assert_equals: offsetLeft expected 0 but got 200 +FAIL end start assert_equals: offsetLeft expected 0 but got 200 +FAIL end center assert_equals: offsetLeft expected 0 but got 200 +FAIL end end assert_equals: offsetLeft expected 0 but got 200 +FAIL self-start self-start assert_equals: offsetLeft expected 0 but got 200 +FAIL self-start center assert_equals: offsetLeft expected 0 but got 200 +FAIL self-start self-end assert_equals: offsetLeft expected 0 but got 200 +FAIL center self-start assert_equals: offsetLeft expected 0 but got 200 +FAIL center self-end assert_equals: offsetLeft expected 0 but got 200 +FAIL self-end self-start assert_equals: offsetLeft expected 0 but got 200 +FAIL self-end center assert_equals: offsetLeft expected 0 but got 200 +FAIL self-end self-end assert_equals: offsetLeft expected 0 but got 200 +FAIL y-start x-start assert_equals: offsetLeft expected 0 but got 200 +FAIL y-start center assert_equals: offsetLeft expected 0 but got 200 +FAIL y-start x-end assert_equals: offsetLeft expected 0 but got 200 +FAIL center x-start assert_equals: offsetLeft expected 0 but got 200 +FAIL center x-end assert_equals: offsetLeft expected 0 but got 200 +FAIL y-end x-start assert_equals: offsetLeft expected 0 but got 200 +FAIL y-end center assert_equals: offsetLeft expected 0 but got 200 +FAIL y-end x-end assert_equals: offsetLeft expected 0 but got 200 +FAIL y-self-start x-self-start assert_equals: offsetLeft expected 0 but got 200 +FAIL y-self-start center assert_equals: offsetLeft expected 0 but got 200 +FAIL y-self-start x-self-end assert_equals: offsetLeft expected 0 but got 200 +FAIL center x-self-start assert_equals: offsetLeft expected 0 but got 200 +FAIL center x-self-end assert_equals: offsetLeft expected 0 but got 200 +FAIL y-self-end x-self-start assert_equals: offsetLeft expected 0 but got 200 +FAIL y-self-end center assert_equals: offsetLeft expected 0 but got 200 +FAIL y-self-end x-self-end assert_equals: offsetLeft expected 0 but got 200 +FAIL span-y-self-start span-x-self-end assert_equals: offsetLeft expected 0 but got 200 +FAIL span-bottom span-all assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --top assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --left assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --right, --top assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --bottom, --top assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --bottom, --right, --top assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --bottom, --right, --left, --top assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --bottom, --left, --top, --right assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --right flip-inline assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --bottom flip-block assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --left flip-start assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --left flip-inline, --top assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --top flip-block, --left assert_equals: offsetLeft expected 0 but got 200 +FAIL Placement: --left flip-start flip-block, --left assert_equals: offsetLeft expected 0 but got 200 diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/property-interpolations-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/property-interpolations-expected.txt index e48251d15b63a..4e663008eb708 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/property-interpolations-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/property-interpolations-expected.txt @@ -251,48 +251,48 @@ PASS Web Animations: property from [left] to [right] at (0.5) sh PASS Web Animations: property from [left] to [right] at (0.6) should be [right] PASS Web Animations: property from [left] to [right] at (1) should be [right] PASS Web Animations: property from [left] to [right] at (1.5) should be [right] -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (-0.3) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (0) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (0.3) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (0.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (0.6) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (1) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (1.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (-0.3) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (0) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (0.3) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (0.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (0.6) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (1) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (1.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions: property from [none] to [--foo] at (-0.3) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions: property from [none] to [--foo] at (0) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions: property from [none] to [--foo] at (0.3) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions: property from [none] to [--foo] at (0.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions: property from [none] to [--foo] at (0.6) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions: property from [none] to [--foo] at (1) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions: property from [none] to [--foo] at (1.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [none] to [--foo] at (-0.3) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [none] to [--foo] at (0) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [none] to [--foo] at (0.3) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [none] to [--foo] at (0.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [none] to [--foo] at (0.6) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [none] to [--foo] at (1) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [none] to [--foo] at (1.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Animations: property from [none] to [--foo] at (-0.3) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL CSS Animations: property from [none] to [--foo] at (0) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL CSS Animations: property from [none] to [--foo] at (0.3) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL CSS Animations: property from [none] to [--foo] at (0.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Animations: property from [none] to [--foo] at (0.6) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Animations: property from [none] to [--foo] at (1) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL CSS Animations: property from [none] to [--foo] at (1.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL Web Animations: property from [none] to [--foo] at (-0.3) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL Web Animations: property from [none] to [--foo] at (0) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL Web Animations: property from [none] to [--foo] at (0.3) should be [none] assert_true: 'to' value should be supported expected true got false -FAIL Web Animations: property from [none] to [--foo] at (0.5) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL Web Animations: property from [none] to [--foo] at (0.6) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL Web Animations: property from [none] to [--foo] at (1) should be [--foo] assert_true: 'to' value should be supported expected true got false -FAIL Web Animations: property from [none] to [--foo] at (1.5) should be [--foo] assert_true: 'to' value should be supported expected true got false +PASS CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (-0.3) should be [none] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (0) should be [none] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (0.3) should be [none] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (0.5) should be [--foo] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (0.6) should be [--foo] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (1) should be [--foo] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [none] to [--foo] at (1.5) should be [--foo] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (-0.3) should be [none] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (0) should be [none] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (0.3) should be [none] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (0.5) should be [--foo] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (0.6) should be [--foo] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (1) should be [--foo] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [none] to [--foo] at (1.5) should be [--foo] +PASS CSS Transitions: property from [none] to [--foo] at (-0.3) should be [--foo] +PASS CSS Transitions: property from [none] to [--foo] at (0) should be [--foo] +PASS CSS Transitions: property from [none] to [--foo] at (0.3) should be [--foo] +PASS CSS Transitions: property from [none] to [--foo] at (0.5) should be [--foo] +PASS CSS Transitions: property from [none] to [--foo] at (0.6) should be [--foo] +PASS CSS Transitions: property from [none] to [--foo] at (1) should be [--foo] +PASS CSS Transitions: property from [none] to [--foo] at (1.5) should be [--foo] +PASS CSS Transitions with transition: all: property from [none] to [--foo] at (-0.3) should be [--foo] +PASS CSS Transitions with transition: all: property from [none] to [--foo] at (0) should be [--foo] +PASS CSS Transitions with transition: all: property from [none] to [--foo] at (0.3) should be [--foo] +PASS CSS Transitions with transition: all: property from [none] to [--foo] at (0.5) should be [--foo] +PASS CSS Transitions with transition: all: property from [none] to [--foo] at (0.6) should be [--foo] +PASS CSS Transitions with transition: all: property from [none] to [--foo] at (1) should be [--foo] +PASS CSS Transitions with transition: all: property from [none] to [--foo] at (1.5) should be [--foo] +PASS CSS Animations: property from [none] to [--foo] at (-0.3) should be [none] +PASS CSS Animations: property from [none] to [--foo] at (0) should be [none] +PASS CSS Animations: property from [none] to [--foo] at (0.3) should be [none] +PASS CSS Animations: property from [none] to [--foo] at (0.5) should be [--foo] +PASS CSS Animations: property from [none] to [--foo] at (0.6) should be [--foo] +PASS CSS Animations: property from [none] to [--foo] at (1) should be [--foo] +PASS CSS Animations: property from [none] to [--foo] at (1.5) should be [--foo] +PASS Web Animations: property from [none] to [--foo] at (-0.3) should be [none] +PASS Web Animations: property from [none] to [--foo] at (0) should be [none] +PASS Web Animations: property from [none] to [--foo] at (0.3) should be [none] +PASS Web Animations: property from [none] to [--foo] at (0.5) should be [--foo] +PASS Web Animations: property from [none] to [--foo] at (0.6) should be [--foo] +PASS Web Animations: property from [none] to [--foo] at (1) should be [--foo] +PASS Web Animations: property from [none] to [--foo] at (1.5) should be [--foo] PASS CSS Transitions with transition-behavior:allow-discrete: property from [none] to [flip-block] at (-0.3) should be [none] PASS CSS Transitions with transition-behavior:allow-discrete: property from [none] to [flip-block] at (0) should be [none] PASS CSS Transitions with transition-behavior:allow-discrete: property from [none] to [flip-block] at (0.3) should be [none] @@ -377,90 +377,90 @@ PASS Web Animations: property from [flip-inline] to [fl PASS Web Animations: property from [flip-inline] to [flip-block] at (0.6) should be [flip-block] PASS Web Animations: property from [flip-inline] to [flip-block] at (1) should be [flip-block] PASS Web Animations: property from [flip-inline] to [flip-block] at (1.5) should be [flip-block] -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (-0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (0) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (0.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (0.6) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (1) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (1.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (-0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (0) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (0.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (0.6) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (1) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (1.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [--bar] at (-0.3) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [--bar] at (0) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [--bar] at (0.3) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [--bar] at (0.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [--bar] at (0.6) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [--bar] at (1) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [--bar] at (1.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [--bar] at (-0.3) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [--bar] at (0) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [--bar] at (0.3) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [--bar] at (0.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [--bar] at (0.6) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [--bar] at (1) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [--bar] at (1.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [--bar] at (-0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [--bar] at (0) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [--bar] at (0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [--bar] at (0.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [--bar] at (0.6) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [--bar] at (1) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [--bar] at (1.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [--bar] at (-0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [--bar] at (0) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [--bar] at (0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [--bar] at (0.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [--bar] at (0.6) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [--bar] at (1) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [--bar] at (1.5) should be [--bar] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (-0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (0) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (0.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (0.6) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (1) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (1.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (-0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (0) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (0.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (0.6) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (1) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (1.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [flip-block] at (-0.3) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [flip-block] at (0) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [flip-block] at (0.3) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [flip-block] at (0.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [flip-block] at (0.6) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [flip-block] at (1) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions: property from [--foo] to [flip-block] at (1.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [flip-block] at (-0.3) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [flip-block] at (0) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [flip-block] at (0.3) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [flip-block] at (0.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [flip-block] at (0.6) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [flip-block] at (1) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Transitions with transition: all: property from [--foo] to [flip-block] at (1.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [flip-block] at (-0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [flip-block] at (0) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [flip-block] at (0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [flip-block] at (0.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [flip-block] at (0.6) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [flip-block] at (1) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL CSS Animations: property from [--foo] to [flip-block] at (1.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [flip-block] at (-0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [flip-block] at (0) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [flip-block] at (0.3) should be [--foo] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [flip-block] at (0.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [flip-block] at (0.6) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [flip-block] at (1) should be [flip-block] assert_true: 'from' value should be supported expected true got false -FAIL Web Animations: property from [--foo] to [flip-block] at (1.5) should be [flip-block] assert_true: 'from' value should be supported expected true got false +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (-0.3) should be [--foo] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (0) should be [--foo] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (0.3) should be [--foo] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (0.5) should be [--bar] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (0.6) should be [--bar] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (1) should be [--bar] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [--bar] at (1.5) should be [--bar] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (-0.3) should be [--foo] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (0) should be [--foo] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (0.3) should be [--foo] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (0.5) should be [--bar] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (0.6) should be [--bar] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (1) should be [--bar] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [--bar] at (1.5) should be [--bar] +PASS CSS Transitions: property from [--foo] to [--bar] at (-0.3) should be [--bar] +PASS CSS Transitions: property from [--foo] to [--bar] at (0) should be [--bar] +PASS CSS Transitions: property from [--foo] to [--bar] at (0.3) should be [--bar] +PASS CSS Transitions: property from [--foo] to [--bar] at (0.5) should be [--bar] +PASS CSS Transitions: property from [--foo] to [--bar] at (0.6) should be [--bar] +PASS CSS Transitions: property from [--foo] to [--bar] at (1) should be [--bar] +PASS CSS Transitions: property from [--foo] to [--bar] at (1.5) should be [--bar] +PASS CSS Transitions with transition: all: property from [--foo] to [--bar] at (-0.3) should be [--bar] +PASS CSS Transitions with transition: all: property from [--foo] to [--bar] at (0) should be [--bar] +PASS CSS Transitions with transition: all: property from [--foo] to [--bar] at (0.3) should be [--bar] +PASS CSS Transitions with transition: all: property from [--foo] to [--bar] at (0.5) should be [--bar] +PASS CSS Transitions with transition: all: property from [--foo] to [--bar] at (0.6) should be [--bar] +PASS CSS Transitions with transition: all: property from [--foo] to [--bar] at (1) should be [--bar] +PASS CSS Transitions with transition: all: property from [--foo] to [--bar] at (1.5) should be [--bar] +PASS CSS Animations: property from [--foo] to [--bar] at (-0.3) should be [--foo] +PASS CSS Animations: property from [--foo] to [--bar] at (0) should be [--foo] +PASS CSS Animations: property from [--foo] to [--bar] at (0.3) should be [--foo] +PASS CSS Animations: property from [--foo] to [--bar] at (0.5) should be [--bar] +PASS CSS Animations: property from [--foo] to [--bar] at (0.6) should be [--bar] +PASS CSS Animations: property from [--foo] to [--bar] at (1) should be [--bar] +PASS CSS Animations: property from [--foo] to [--bar] at (1.5) should be [--bar] +PASS Web Animations: property from [--foo] to [--bar] at (-0.3) should be [--foo] +PASS Web Animations: property from [--foo] to [--bar] at (0) should be [--foo] +PASS Web Animations: property from [--foo] to [--bar] at (0.3) should be [--foo] +PASS Web Animations: property from [--foo] to [--bar] at (0.5) should be [--bar] +PASS Web Animations: property from [--foo] to [--bar] at (0.6) should be [--bar] +PASS Web Animations: property from [--foo] to [--bar] at (1) should be [--bar] +PASS Web Animations: property from [--foo] to [--bar] at (1.5) should be [--bar] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (-0.3) should be [--foo] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (0) should be [--foo] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (0.3) should be [--foo] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (0.5) should be [flip-block] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (0.6) should be [flip-block] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (1) should be [flip-block] +PASS CSS Transitions with transition-behavior:allow-discrete: property from [--foo] to [flip-block] at (1.5) should be [flip-block] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (-0.3) should be [--foo] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (0) should be [--foo] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (0.3) should be [--foo] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (0.5) should be [flip-block] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (0.6) should be [flip-block] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (1) should be [flip-block] +PASS CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property from [--foo] to [flip-block] at (1.5) should be [flip-block] +PASS CSS Transitions: property from [--foo] to [flip-block] at (-0.3) should be [flip-block] +PASS CSS Transitions: property from [--foo] to [flip-block] at (0) should be [flip-block] +PASS CSS Transitions: property from [--foo] to [flip-block] at (0.3) should be [flip-block] +PASS CSS Transitions: property from [--foo] to [flip-block] at (0.5) should be [flip-block] +PASS CSS Transitions: property from [--foo] to [flip-block] at (0.6) should be [flip-block] +PASS CSS Transitions: property from [--foo] to [flip-block] at (1) should be [flip-block] +PASS CSS Transitions: property from [--foo] to [flip-block] at (1.5) should be [flip-block] +PASS CSS Transitions with transition: all: property from [--foo] to [flip-block] at (-0.3) should be [flip-block] +PASS CSS Transitions with transition: all: property from [--foo] to [flip-block] at (0) should be [flip-block] +PASS CSS Transitions with transition: all: property from [--foo] to [flip-block] at (0.3) should be [flip-block] +PASS CSS Transitions with transition: all: property from [--foo] to [flip-block] at (0.5) should be [flip-block] +PASS CSS Transitions with transition: all: property from [--foo] to [flip-block] at (0.6) should be [flip-block] +PASS CSS Transitions with transition: all: property from [--foo] to [flip-block] at (1) should be [flip-block] +PASS CSS Transitions with transition: all: property from [--foo] to [flip-block] at (1.5) should be [flip-block] +PASS CSS Animations: property from [--foo] to [flip-block] at (-0.3) should be [--foo] +PASS CSS Animations: property from [--foo] to [flip-block] at (0) should be [--foo] +PASS CSS Animations: property from [--foo] to [flip-block] at (0.3) should be [--foo] +PASS CSS Animations: property from [--foo] to [flip-block] at (0.5) should be [flip-block] +PASS CSS Animations: property from [--foo] to [flip-block] at (0.6) should be [flip-block] +PASS CSS Animations: property from [--foo] to [flip-block] at (1) should be [flip-block] +PASS CSS Animations: property from [--foo] to [flip-block] at (1.5) should be [flip-block] +PASS Web Animations: property from [--foo] to [flip-block] at (-0.3) should be [--foo] +PASS Web Animations: property from [--foo] to [flip-block] at (0) should be [--foo] +PASS Web Animations: property from [--foo] to [flip-block] at (0.3) should be [--foo] +PASS Web Animations: property from [--foo] to [flip-block] at (0.5) should be [flip-block] +PASS Web Animations: property from [--foo] to [flip-block] at (0.6) should be [flip-block] +PASS Web Animations: property from [--foo] to [flip-block] at (1) should be [flip-block] +PASS Web Animations: property from [--foo] to [flip-block] at (1.5) should be [flip-block] PASS CSS Transitions with transition-behavior:allow-discrete: property from [normal] to [most-width] at (-0.3) should be [normal] PASS CSS Transitions with transition-behavior:allow-discrete: property from [normal] to [most-width] at (0) should be [normal] PASS CSS Transitions with transition-behavior:allow-discrete: property from [normal] to [most-width] at (0.3) should be [normal] diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/try-tactic-basic-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/try-tactic-basic-expected.txt index ce8fa77a11e57..77961b82c0744 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/try-tactic-basic-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/try-tactic-basic-expected.txt @@ -1,18 +1,18 @@ -FAIL CSS Anchor Positioning: try-tactic assert_equals: offsetLeft expected 10 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 1 assert_equals: offsetLeft expected 10 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 2 assert_equals: offsetLeft expected 360 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 3 assert_equals: offsetLeft expected 360 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 4 assert_equals: offsetLeft expected 360 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 5 assert_equals: offsetLeft expected 20 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 6 assert_equals: offsetLeft expected 20 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 7 assert_equals: offsetLeft expected 20 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 8 assert_equals: offsetLeft expected 20 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 9 assert_equals: offsetLeft expected 20 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 10 assert_equals: offsetLeft expected 340 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 11 assert_equals: offsetLeft expected 340 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 12 assert_equals: offsetLeft expected 340 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 13 assert_equals: offsetLeft expected 340 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 14 assert_equals: offsetLeft expected 340 but got 99999 -FAIL CSS Anchor Positioning: try-tactic 15 assert_equals: offsetLeft expected 340 but got 99999 +FAIL --pf assert_equals: offsetLeft expected 10 but got 99999 +FAIL --pf flip-block assert_equals: offsetLeft expected 10 but got 99999 +FAIL --pf flip-inline assert_equals: offsetLeft expected 360 but got 99999 +FAIL --pf flip-block flip-inline assert_equals: offsetLeft expected 360 but got 99999 +FAIL --pf flip-inline flip-block assert_equals: offsetLeft expected 360 but got 99999 +FAIL --pf flip-start assert_equals: offsetLeft expected 20 but got 99999 +FAIL --pf flip-block flip-start flip-inline assert_equals: offsetLeft expected 20 but got 99999 +FAIL --pf flip-inline flip-start flip-block assert_equals: offsetLeft expected 20 but got 99999 +FAIL --pf flip-start flip-block assert_equals: offsetLeft expected 20 but got 99999 +FAIL --pf flip-inline flip-start assert_equals: offsetLeft expected 20 but got 99999 +FAIL --pf flip-start flip-inline assert_equals: offsetLeft expected 340 but got 99999 +FAIL --pf flip-block flip-start assert_equals: offsetLeft expected 340 but got 99999 +FAIL --pf flip-start flip-block flip-inline assert_equals: offsetLeft expected 340 but got 99999 +FAIL --pf flip-start flip-inline flip-block assert_equals: offsetLeft expected 340 but got 99999 +FAIL --pf flip-inline flip-block flip-start assert_equals: offsetLeft expected 340 but got 99999 +FAIL --pf flip-block flip-inline flip-start assert_equals: offsetLeft expected 340 but got 99999 diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/try-tactic-margin-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/try-tactic-margin-expected.txt index 0e782d7a47192..285d82cbb234b 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/try-tactic-margin-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-anchor-position/try-tactic-margin-expected.txt @@ -1,10 +1,10 @@ -FAIL CSS Anchor Positioning: try-tactic (margin) assert_equals: offsetLeft expected 45 but got 99999 -FAIL CSS Anchor Positioning: try-tactic (margin) 1 assert_equals: offsetLeft expected 45 but got 99999 -FAIL CSS Anchor Positioning: try-tactic (margin) 2 assert_equals: offsetLeft expected 325 but got 99999 -FAIL CSS Anchor Positioning: try-tactic (margin) 3 assert_equals: offsetLeft expected 325 but got 99999 -FAIL CSS Anchor Positioning: try-tactic (margin) 4 assert_equals: offsetLeft expected 25 but got 99999 -FAIL CSS Anchor Positioning: try-tactic (margin) 5 assert_equals: offsetLeft expected 335 but got 99999 -FAIL CSS Anchor Positioning: try-tactic (margin) 6 assert_equals: offsetLeft expected 25 but got 99999 -FAIL CSS Anchor Positioning: try-tactic (margin) 7 assert_equals: offsetLeft expected 335 but got 99999 +FAIL --pf assert_equals: offsetLeft expected 45 but got 99999 +FAIL --pf flip-block assert_equals: offsetLeft expected 45 but got 99999 +FAIL --pf flip-inline assert_equals: offsetLeft expected 325 but got 99999 +FAIL --pf flip-block flip-inline assert_equals: offsetLeft expected 325 but got 99999 +FAIL --pf flip-start assert_equals: offsetLeft expected 25 but got 99999 +FAIL --pf flip-block flip-start assert_equals: offsetLeft expected 335 but got 99999 +FAIL --pf flip-inline flip-start assert_equals: offsetLeft expected 25 but got 99999 +FAIL --pf flip-block flip-inline flip-start assert_equals: offsetLeft expected 335 but got 99999 diff --git a/Source/WebCore/css/ComputedStyleExtractor.cpp b/Source/WebCore/css/ComputedStyleExtractor.cpp index 0b1b1ef635708..1d8fd63048713 100644 --- a/Source/WebCore/css/ComputedStyleExtractor.cpp +++ b/Source/WebCore/css/ComputedStyleExtractor.cpp @@ -1076,6 +1076,13 @@ static Ref computedRotate(RenderObject* renderer, const RenderStyle& s CSSPrimitiveValue::create(rotate->y()), CSSPrimitiveValue::create(rotate->z()), WTFMove(angle)); } +static Ref valueForScopedName(const Style::ScopedName& scopedName) +{ + if (scopedName.isIdentifier) + return CSSPrimitiveValue::createCustomIdent(scopedName.name); + return CSSPrimitiveValue::create(scopedName.name); +} + static Ref valueForBoxShadow(const ShadowData* shadow, const RenderStyle& style) { if (!shadow) @@ -1113,10 +1120,12 @@ static Ref valueForPositionTryFallbacks(const Vector valueForAnimationPlayState(AnimationPlayState play RELEASE_ASSERT_NOT_REACHED(); } -static Ref valueForScopedName(const Style::ScopedName& scopedName) -{ - if (scopedName.isIdentifier) - return CSSPrimitiveValue::createCustomIdent(scopedName.name); - return CSSPrimitiveValue::create(scopedName.name); -} - static Ref valueForAnimationTimeline(const RenderStyle& style, const Animation::Timeline& timeline) { auto valueForAnonymousScrollTimeline = [](const Animation::AnonymousScrollTimeline& anonymousScrollTimeline) { diff --git a/Source/WebCore/css/parser/CSSPropertyParserConsumer+PositionTry.cpp b/Source/WebCore/css/parser/CSSPropertyParserConsumer+PositionTry.cpp index 3c44efc0f63fb..ca2e37892b34f 100644 --- a/Source/WebCore/css/parser/CSSPropertyParserConsumer+PositionTry.cpp +++ b/Source/WebCore/css/parser/CSSPropertyParserConsumer+PositionTry.cpp @@ -36,19 +36,32 @@ namespace CSSPropertyParserHelpers { RefPtr consumePositionTryFallbacks(CSSParserTokenRange& range, const CSSParserContext&) { + // none | [ [ || ] | <'position-area'> ]# + // FIXME: Implement <'position-area'> if (auto result = consumeIdent(range)) return result; auto consume = [](CSSParserTokenRange& range) -> RefPtr { + // [ || ] + auto tryRuleIdent = consumeDashedIdentRaw(range); + Vector idents; while (auto ident = consumeIdentRaw(range)) { if (idents.contains(*ident)) return nullptr; idents.append(*ident); } + CSSValueListBuilder list; for (auto ident : idents) list.append(CSSPrimitiveValue::create(ident)); + + if (tryRuleIdent.isNull()) + tryRuleIdent = consumeDashedIdentRaw(range); + + if (!tryRuleIdent.isNull()) + list.insert(0, CSSPrimitiveValue::createCustomIdent(tryRuleIdent)); + return CSSValueList::createSpaceSeparated(WTFMove(list)); }; return consumeCommaSeparatedListWithSingleValueOptimization(range, consume); diff --git a/Source/WebCore/rendering/style/PositionTryFallback.h b/Source/WebCore/rendering/style/PositionTryFallback.h index f2615e21c9992..533fe5728f760 100644 --- a/Source/WebCore/rendering/style/PositionTryFallback.h +++ b/Source/WebCore/rendering/style/PositionTryFallback.h @@ -25,12 +25,15 @@ #pragma once +#include "ScopedName.h" #include #include namespace WebCore { struct PositionTryFallback { + std::optional positionTryRuleName; + enum class Tactic : uint8_t { FlipBlock, FlipInline, diff --git a/Source/WebCore/style/StyleBuilderConverter.h b/Source/WebCore/style/StyleBuilderConverter.h index ef9e4fb11b749..568959311bc13 100644 --- a/Source/WebCore/style/StyleBuilderConverter.h +++ b/Source/WebCore/style/StyleBuilderConverter.h @@ -2718,16 +2718,21 @@ inline NameScope BuilderConverter::convertNameScope(BuilderState& builderState, }) }; } -inline Vector BuilderConverter::convertPositionTryFallbacks(BuilderState&, const CSSValue& value) +inline Vector BuilderConverter::convertPositionTryFallbacks(BuilderState& builderState, const CSSValue& value) { auto fallbackForValueList = [&](const CSSValueList& valueList) -> std::optional { if (valueList.separator() != CSSValueList::SpaceSeparator) return { }; - auto tactics = WTF::map(valueList, [&](auto& item) { - return fromCSSValueID(item.valueID()); - }); - return PositionTryFallback { .tactics = WTFMove(tactics) }; + auto fallback = PositionTryFallback { }; + + for (auto& item : valueList) { + if (item.isCustomIdent()) + fallback.positionTryRuleName = Style::ScopedName { AtomString { item.customIdent() }, builderState.styleScopeOrdinal() }; + else + fallback.tactics.append(fromCSSValueID(item.valueID())); + } + return fallback; }; if (auto* primitiveValue = dynamicDowncast(value)) { diff --git a/Source/WebCore/style/StyleTreeResolver.cpp b/Source/WebCore/style/StyleTreeResolver.cpp index 2b4198ac40c85..bfc2a87264959 100644 --- a/Source/WebCore/style/StyleTreeResolver.cpp +++ b/Source/WebCore/style/StyleTreeResolver.cpp @@ -1358,15 +1358,17 @@ std::optional TreeResolver::tryChoosePositionOption(const Styleab { // https://drafts.csswg.org/css-anchor-position-1/#fallback-apply - if (!existingStyle) - return { }; - auto optionIt = m_positionOptions.find(styleable.element); if (optionIt == m_positionOptions.end()) return { }; auto& options = optionIt->value; + if (!existingStyle) { + options.chosen = true; + return ResolvedStyle { RenderStyle::clonePtr(*options.originalStyle) }; + } + auto renderer = dynamicDowncast(styleable.element.renderer()); if (!renderer) { options.chosen = true; From f35d14581734514f44bf60f05ca8089e3dee3fee Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Sun, 23 Feb 2025 10:36:53 -0800 Subject: [PATCH 091/174] Address safer cpp failures in SubresourceLoader.cpp https://bugs.webkit.org/show_bug.cgi?id=288277 Reviewed by Geoffrey Garen. * Source/WebCore/loader/SubresourceLoader.cpp: (WebCore::SubresourceLoader::SubresourceLoader): (WebCore::SubresourceLoader::willSendRequestInternal): (WebCore::SubresourceLoader::didReceiveResponse): (WebCore::SubresourceLoader::reportResourceTiming): Canonical link: https://commits.webkit.org/290912@main --- .../UncountedCallArgsCheckerExpectations | 1 - .../UncountedLambdaCapturesCheckerExpectations | 1 - Source/WebCore/loader/SubresourceLoader.cpp | 15 ++++++++------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations index 6769a54aaa747..81eb300870b43 100644 --- a/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations @@ -894,7 +894,6 @@ loader/PolicyChecker.cpp loader/ResourceLoadNotifier.cpp loader/ResourceLoader.cpp loader/SubframeLoader.cpp -loader/SubresourceLoader.cpp loader/WorkerThreadableLoader.cpp loader/appcache/ApplicationCache.cpp loader/appcache/ApplicationCacheGroup.cpp diff --git a/Source/WebCore/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations index 04582eb8d4158..dec6338efdf54 100644 --- a/Source/WebCore/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncountedLambdaCapturesCheckerExpectations @@ -114,7 +114,6 @@ inspector/agents/page/PageDOMDebuggerAgent.cpp loader/FrameLoader.cpp loader/NetscapePlugInStreamLoader.cpp loader/ResourceLoader.cpp -loader/SubresourceLoader.cpp page/EventSource.cpp page/ImageAnalysisQueue.cpp page/LocalDOMWindow.cpp diff --git a/Source/WebCore/loader/SubresourceLoader.cpp b/Source/WebCore/loader/SubresourceLoader.cpp index 33f7cf09f718c..8d8b4d17eaa83 100644 --- a/Source/WebCore/loader/SubresourceLoader.cpp +++ b/Source/WebCore/loader/SubresourceLoader.cpp @@ -119,7 +119,7 @@ SubresourceLoader::SubresourceLoader(LocalFrame& frame, CachedResource& resource : ResourceLoader(frame, options) , m_resource(resource) , m_state(Uninitialized) - , m_requestCountTracker(std::in_place, frame.document()->cachedResourceLoader(), resource) + , m_requestCountTracker(std::in_place, frame.protectedDocument()->cachedResourceLoader(), resource) { #ifndef NDEBUG subresourceLoaderCounter.increment(); @@ -231,7 +231,7 @@ void SubresourceLoader::willSendRequestInternal(ResourceRequest&& newRequest, co return completionHandler(WTFMove(newRequest)); } - ResourceLoader::willSendRequestInternal(WTFMove(newRequest), redirectResponse, [this, protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler), redirectResponse] (ResourceRequest&& request) mutable { + ResourceLoader::willSendRequestInternal(WTFMove(newRequest), redirectResponse, [this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler), redirectResponse] (ResourceRequest&& request) mutable { tracePoint(SubresourceLoadWillStart, identifier() ? identifier()->toUInt64() : 0, PAGE_ID, FRAME_ID); if (reachedTerminalState()) { @@ -336,7 +336,7 @@ void SubresourceLoader::willSendRequestInternal(ResourceRequest&& newRequest, co cancel(); return completionHandler(WTFMove(newRequest)); } - resource->redirectReceived(WTFMove(newRequest), redirectResponse, [this, protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler), continueWillSendRequest = WTFMove(continueWillSendRequest)] (ResourceRequest&& request) mutable { + resource->redirectReceived(WTFMove(newRequest), redirectResponse, [this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler), continueWillSendRequest = WTFMove(continueWillSendRequest)] (ResourceRequest&& request) mutable { SUBRESOURCELOADER_RELEASE_LOG(SUBRESOURCELOADER_WILLSENDREQUESTINTERNAL_RESOURCE_DONE_NOTIFYING_CLIENTS); continueWillSendRequest(WTFMove(completionHandler), WTFMove(request)); }); @@ -490,7 +490,7 @@ void SubresourceLoader::didReceiveResponse(const ResourceResponse& response, Com if (resource) { resource->responseReceived(m_previousPartResponse); // The resource data will change as the next part is loaded, so we need to make a copy. - resource->finishLoading(resourceData()->copy().ptr(), { }); + resource->finishLoading(protectedResourceData()->copy().ptr(), { }); } } clearResourceData(); @@ -510,7 +510,7 @@ void SubresourceLoader::didReceiveResponse(const ResourceResponse& response, Com bool isResponseMultipart = response.isMultipart(); if (options().mode != FetchOptions::Mode::Navigate && frame && frame->document()) LinkLoader::loadLinksFromHeader(response.httpHeaderField(HTTPHeaderName::Link), protectedDocumentLoader()->url(), *frame->protectedDocument(), LinkLoader::MediaAttributeCheck::SkipMediaAttributeCheck); - ResourceLoader::didReceiveResponse(response, [this, protectedThis = WTFMove(protectedThis), isResponseMultipart, completionHandlerCaller = WTFMove(completionHandlerCaller)]() mutable { + ResourceLoader::didReceiveResponse(response, [this, protectedThis = Ref { *this }, isResponseMultipart, completionHandlerCaller = WTFMove(completionHandlerCaller)]() mutable { if (reachedTerminalState()) return; @@ -930,7 +930,8 @@ void SubresourceLoader::reportResourceTiming(const NetworkLoadMetrics& networkLo if (!resource || !ResourceTimingInformation::shouldAddResourceTiming(*resource)) return; - RefPtr document = protectedDocumentLoader()->cachedResourceLoader().document(); + RefPtr documentLoader = this->documentLoader(); + RefPtr document = documentLoader->cachedResourceLoader().document(); if (!document) return; @@ -947,7 +948,7 @@ void SubresourceLoader::reportResourceTiming(const NetworkLoadMetrics& networkLo } ASSERT(options().initiatorContext == InitiatorContext::Document); - documentLoader()->protectedCachedResourceLoader()->resourceTimingInformation().addResourceTiming(*protectedCachedResource(), *document, WTFMove(resourceTiming)); + documentLoader->protectedCachedResourceLoader()->resourceTimingInformation().addResourceTiming(*protectedCachedResource(), *document, WTFMove(resourceTiming)); } const HTTPHeaderMap* SubresourceLoader::originalHeaders() const From 49c3a261490aec6dcf7ce8b4f6b5880009dabe07 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Sun, 23 Feb 2025 10:39:06 -0800 Subject: [PATCH 092/174] Address safer cpp failures in RenderView.cpp https://bugs.webkit.org/show_bug.cgi?id=288295 Reviewed by Alan Baradlay. * Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations: * Source/WebCore/SaferCPPExpectations/UncheckedLocalVarsCheckerExpectations: * Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations: * Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations: * Source/WebCore/platform/ScrollView.h: (WebCore::ScrollView::protectedHorizontalScrollbar const): (WebCore::ScrollView::protectedVerticalScrollbar const): * Source/WebCore/rendering/RenderView.cpp: (WebCore::RenderView::styleDidChange): (WebCore::RenderView::updateQuirksMode): (WebCore::RenderView::updateInitialContainingBlockSize): (WebCore::RenderView::clientLogicalWidthForFixedPosition const): (WebCore::RenderView::clientLogicalHeightForFixedPosition const): (WebCore::RenderView::rendererForRootBackground const): (WebCore::RenderView::paintBoxDecorations): (WebCore::RenderView::repaintViewRectangle const): (WebCore::RenderView::shouldUsePrintingLayout const): (WebCore::RenderView::shouldPaintBaseBackground const): (WebCore::RenderView::rootElementShouldPaintBaseBackground const): (WebCore::RenderView::updatePlayStateForAllAnimations): (WebCore::RenderView::RepaintRegionAccumulator::RepaintRegionAccumulator): * Source/WebCore/rendering/RenderViewTransitionCapture.cpp: (WebCore::RenderViewTransitionCapture::paintReplaced): Canonical link: https://commits.webkit.org/290913@main --- .../UncheckedCallArgsCheckerExpectations | 1 - .../UncheckedLocalVarsCheckerExpectations | 1 - .../UncountedCallArgsCheckerExpectations | 2 - .../UncountedLocalVarsCheckerExpectations | 1 - Source/WebCore/platform/ScrollView.h | 2 + Source/WebCore/rendering/RenderView.cpp | 62 ++++++++++--------- .../rendering/RenderViewTransitionCapture.cpp | 4 +- 7 files changed, 36 insertions(+), 37 deletions(-) diff --git a/Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations index 604ca3e756079..3a986347f7605 100644 --- a/Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations @@ -84,7 +84,6 @@ rendering/RenderScrollbar.cpp rendering/RenderScrollbarPart.cpp rendering/RenderSearchField.cpp rendering/RenderTreeAsText.cpp -rendering/RenderView.cpp rendering/RenderWidget.cpp rendering/svg/RenderSVGRoot.cpp rendering/svg/SVGPaintServerHandling.h diff --git a/Source/WebCore/SaferCPPExpectations/UncheckedLocalVarsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncheckedLocalVarsCheckerExpectations index 5eb643b6cb2a4..39370d775f7dc 100644 --- a/Source/WebCore/SaferCPPExpectations/UncheckedLocalVarsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncheckedLocalVarsCheckerExpectations @@ -69,7 +69,6 @@ rendering/RenderObject.cpp rendering/RenderReplaced.cpp rendering/RenderTextControlSingleLine.cpp rendering/RenderTreeAsText.cpp -rendering/RenderView.cpp rendering/RenderWidget.cpp style/StyleResolver.cpp testing/Internals.cpp diff --git a/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations index 81eb300870b43..fa7fee285987f 100644 --- a/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations @@ -1213,8 +1213,6 @@ rendering/RenderTheme.cpp rendering/RenderTreeAsText.cpp rendering/RenderVTTCue.cpp rendering/RenderVideo.cpp -rendering/RenderView.cpp -rendering/RenderViewTransitionCapture.cpp rendering/RenderWidget.cpp rendering/TextBoxPainter.cpp rendering/TextPaintStyle.cpp diff --git a/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations index 2e5bf5106a97d..16b3179ffa68b 100644 --- a/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations @@ -664,7 +664,6 @@ rendering/RenderTextControlSingleLine.cpp rendering/RenderTextControlSingleLine.h rendering/RenderTheme.cpp rendering/RenderTreeAsText.cpp -rendering/RenderView.cpp rendering/RenderWidget.cpp rendering/TextBoxPainter.cpp rendering/TextPaintStyle.cpp diff --git a/Source/WebCore/platform/ScrollView.h b/Source/WebCore/platform/ScrollView.h index a62886706a2c9..46ecf0d94cc31 100644 --- a/Source/WebCore/platform/ScrollView.h +++ b/Source/WebCore/platform/ScrollView.h @@ -109,7 +109,9 @@ class ScrollView : public Widget, public ScrollableArea, public CanMakeCheckedPt // If the scroll view does not use a native widget, then it will have cross-platform Scrollbars. These functions // can be used to obtain those scrollbars. Scrollbar* horizontalScrollbar() const final { return m_horizontalScrollbar.get(); } + RefPtr protectedHorizontalScrollbar() const { return horizontalScrollbar(); } Scrollbar* verticalScrollbar() const final { return m_verticalScrollbar.get(); } + RefPtr protectedVerticalScrollbar() const { return verticalScrollbar(); } bool isScrollViewScrollbar(const Widget* child) const { return horizontalScrollbar() == child || verticalScrollbar() == child; } void positionScrollbarLayers(); diff --git a/Source/WebCore/rendering/RenderView.cpp b/Source/WebCore/rendering/RenderView.cpp index 12f8d043564d3..414c6ee014c50 100644 --- a/Source/WebCore/rendering/RenderView.cpp +++ b/Source/WebCore/rendering/RenderView.cpp @@ -124,7 +124,7 @@ void RenderView::styleDidChange(StyleDifference diff, const RenderStyle* oldStyl } if (directionChanged) - frameView().topContentDirectionDidChange(); + protectedFrameView()->topContentDirectionDidChange(); } RenderBox::LogicalExtentComputedValues RenderView::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit) const @@ -211,13 +211,13 @@ void RenderView::layout() void RenderView::updateQuirksMode() { - m_layoutState->updateQuirksMode(document()); + m_layoutState->updateQuirksMode(protectedDocument()); } void RenderView::updateInitialContainingBlockSize() { // Initial containing block has no margin/padding/border. - m_layoutState->ensureGeometryForBox(m_initialContainingBlock).setContentBoxSize(frameView().size()); + m_layoutState->ensureGeometryForBox(m_initialContainingBlock).setContentBoxSize(protectedFrameView()->size()); } LayoutUnit RenderView::pageOrViewLogicalHeight() const @@ -237,7 +237,7 @@ LayoutUnit RenderView::clientLogicalWidthForFixedPosition() const { Ref frameView = this->frameView(); if (frameView->fixedElementsLayoutRelativeToFrame()) - return LayoutUnit((isHorizontalWritingMode() ? frameView->visibleWidth() : frameView->visibleHeight()) / frameView->frame().frameScaleFactor()); + return LayoutUnit((isHorizontalWritingMode() ? frameView->visibleWidth() : frameView->visibleHeight()) / frameView->protectedFrame()->frameScaleFactor()); #if PLATFORM(IOS_FAMILY) if (frameView->useCustomFixedPositionLayoutRect()) @@ -254,7 +254,7 @@ LayoutUnit RenderView::clientLogicalHeightForFixedPosition() const { Ref frameView = this->frameView(); if (frameView->fixedElementsLayoutRelativeToFrame()) - return LayoutUnit((isHorizontalWritingMode() ? frameView->visibleHeight() : frameView->visibleWidth()) / frameView->frame().frameScaleFactor()); + return LayoutUnit((isHorizontalWritingMode() ? frameView->visibleHeight() : frameView->visibleWidth()) / frameView->protectedFrame()->frameScaleFactor()); #if PLATFORM(IOS_FAMILY) if (frameView->useCustomFixedPositionLayoutRect()) @@ -361,7 +361,7 @@ RenderElement* RenderView::rendererForRootBackground() const if (documentRenderer.shouldApplyAnyContainment()) return nullptr; - if (auto* body = document().body()) { + if (RefPtr body = protectedDocument()->body()) { if (auto* renderer = body->renderer()) { if (!renderer->shouldApplyAnyContainment()) return renderer; @@ -405,7 +405,8 @@ void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&) // layers with reflections, or transformed layers. // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being inside // a transform, transparency layer, etc. - for (HTMLFrameOwnerElement* element = document().ownerElement(); element && element->renderer(); element = element->document().ownerElement()) { + Ref document = this->document(); + for (RefPtr element = document->ownerElement(); element && element->renderer(); element = element->protectedDocument()->ownerElement()) { RenderLayer* layer = element->renderer()->enclosingLayer(); if (layer->cannotBlitToWindow()) { protectedFrameView()->setCannotBlitToWindow(); @@ -429,7 +430,7 @@ void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&) bool rootFillsViewport = false; bool rootObscuresBackground = false; auto shouldPropagateBackgroundPaintingToInitialContainingBlock = true; - Element* documentElement = document().documentElement(); + RefPtr documentElement = document->documentElement(); if (RenderElement* rootRenderer = documentElement ? documentElement->renderer() : nullptr) { // The document element's renderer is currently forced to be a block, but may not always be. auto* rootBox = dynamicDowncast(*rootRenderer); @@ -440,7 +441,7 @@ void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&) compositor().rootBackgroundColorOrTransparencyChanged(); - Page* page = document().page(); + RefPtr page = document->page(); float pageScaleFactor = page ? page->pageScaleFactor() : 1; // If painting will entirely fill the view, no need to fill the background. @@ -494,7 +495,8 @@ void RenderView::repaintViewRectangle(const LayoutRect& repaintRect) const // FIXME: enclosingRect is needed as long as we integral snap ScrollView/FrameView/RenderWidget size/position. auto enclosingRect = enclosingIntRect(repaintRect); - if (auto ownerElement = document().ownerElement()) { + Ref document = this->document(); + if (RefPtr ownerElement = document->ownerElement()) { auto* ownerBox = ownerElement->renderBox(); if (!ownerBox) return; @@ -519,13 +521,13 @@ void RenderView::repaintViewRectangle(const LayoutRect& repaintRect) const // left scrollbar (if one exists). Ref frameView = this->frameView(); if (frameView->shouldPlaceVerticalScrollbarOnLeft() && frameView->verticalScrollbar()) - adjustedRect.move(LayoutSize(frameView->verticalScrollbar()->occupiedWidth(), 0)); + adjustedRect.move(LayoutSize(frameView->protectedVerticalScrollbar()->occupiedWidth(), 0)); ownerBox->repaintRectangle(adjustedRect); return; } - protectedFrameView()->addTrackedRepaintRect(snapRectToDevicePixels(repaintRect, document().deviceScaleFactor())); + protectedFrameView()->addTrackedRepaintRect(snapRectToDevicePixels(repaintRect, document->deviceScaleFactor())); if (!m_accumulatedRepaintRegion) { protectedFrameView()->repaintContentRectangle(enclosingRect); return; @@ -578,7 +580,7 @@ auto RenderView::computeVisibleRectsInContainer(const RepaintRects& rects, const // Apply our transform if we have one (because of full page zooming). if (!container && hasLayer() && layer()->transform()) - adjustedRects.transform(*layer()->transform(), document().deviceScaleFactor()); + adjustedRects.transform(*layer()->transform(), protectedDocument()->deviceScaleFactor()); return adjustedRects; } @@ -613,7 +615,7 @@ bool RenderView::shouldUsePrintingLayout() const { if (!printing()) return false; - return protectedFrameView()->frame().shouldUsePrintingLayout(); + return protectedFrameView()->protectedFrame()->shouldUsePrintingLayout(); } LayoutRect RenderView::viewRect() const @@ -639,13 +641,13 @@ bool RenderView::rootBackgroundIsEntirelyFixed() const bool RenderView::shouldPaintBaseBackground() const { - auto& document = this->document(); - auto& frameView = this->frameView(); - auto* ownerElement = document.ownerElement(); + Ref document = this->document(); + Ref frameView = this->frameView(); + RefPtr ownerElement = document->ownerElement(); // Fill with a base color if we're the root document. - if (frameView.frame().isMainFrame()) - return !frameView.isTransparent(); + if (frameView->frame().isMainFrame()) + return !frameView->isTransparent(); if (ownerElement && ownerElement->hasTagName(HTMLNames::frameTag)) return true; @@ -654,11 +656,11 @@ bool RenderView::shouldPaintBaseBackground() const // to crawl around a render tree with potential :before/:after content and // anonymous blocks created by inline tags etc. We can locate the // render object very easily via the DOM. - auto* body = document.bodyOrFrameset(); + RefPtr body = document->bodyOrFrameset(); // SVG documents and XML documents with SVG root nodes are transparent. if (!body) - return !document.hasSVGRootNode(); + return !document->hasSVGRootNode(); // Can't scroll a frameset document anyway. if (is(*body)) @@ -671,15 +673,15 @@ bool RenderView::shouldPaintBaseBackground() const // iframes should fill with a base color if the used color scheme of the // element and the used color scheme of the embedded document’s root // element do not match. - if (frameView.useDarkAppearance() != frameRenderer->useDarkAppearance()) - return !frameView.isTransparent(); + if (frameView->useDarkAppearance() != frameRenderer->useDarkAppearance()) + return !frameView->isTransparent(); return false; } bool RenderView::rootElementShouldPaintBaseBackground() const { - auto* documentElement = document().documentElement(); + RefPtr documentElement = document().documentElement(); if (RenderElement* rootRenderer = documentElement ? documentElement->renderer() : nullptr) { // The document element's renderer is currently forced to be a block, but may not always be. auto* rootBox = dynamicDowncast(*rootRenderer); @@ -960,8 +962,8 @@ void RenderView::updatePlayStateForAllAnimations(const IntRect& visibleRect) return; bool hasPausedAnimation = renderElement.hasPausedImageAnimations(); - auto* image = cachedImage->image(); - if (auto* svgImage = dynamicDowncast(image)) { + RefPtr image = cachedImage->image(); + if (RefPtr svgImage = dynamicDowncast(image.get())) { if (shouldAnimate && hasPausedAnimation) { svgImage->resumeAnimation(); removeRendererWithPausedImageAnimations(renderElement, *cachedImage); @@ -971,7 +973,7 @@ void RenderView::updatePlayStateForAllAnimations(const IntRect& visibleRect) } } else if (image && image->isAnimated()) { // Override any individual animation play state that may have been set. - if (auto* imageElement = dynamicDowncast(renderElement.element())) + if (RefPtr imageElement = dynamicDowncast(renderElement.element())) imageElement->setAllowsAnimation(std::nullopt); else image->setAllowsAnimation(std::nullopt); @@ -987,7 +989,7 @@ void RenderView::updatePlayStateForAllAnimations(const IntRect& visibleRect) } }; - for (const auto* layer = &renderElement.style().backgroundLayers(); layer; layer = layer->next()) + for (RefPtr layer = &renderElement.style().backgroundLayers(); layer; layer = layer->next()) updateAnimation(layer->image() ? layer->image()->cachedImage() : nullptr); if (auto* renderImage = dynamicDowncast(renderElement)) @@ -996,7 +998,7 @@ void RenderView::updatePlayStateForAllAnimations(const IntRect& visibleRect) if (needsRepaint) renderElement.repaint(); - if (auto* svgSvgElement = svgSvgElementFrom(renderElement)) { + if (RefPtr svgSvgElement = svgSvgElementFrom(renderElement)) { if (shouldAnimate) { svgSvgElement->unpauseAnimations(); m_SVGSVGElementsWithPausedImageAnimation.remove(*svgSvgElement); @@ -1014,7 +1016,7 @@ RenderView::RepaintRegionAccumulator::RepaintRegionAccumulator(RenderView* view) if (!view) return; - if (!view->document().isTopDocument()) + if (!view->protectedDocument()->isTopDocument()) return; m_wasAccumulatingRepaintRegion = !!view->m_accumulatedRepaintRegion; diff --git a/Source/WebCore/rendering/RenderViewTransitionCapture.cpp b/Source/WebCore/rendering/RenderViewTransitionCapture.cpp index 3ff65b6526189..942acbe6cfe5c 100644 --- a/Source/WebCore/rendering/RenderViewTransitionCapture.cpp +++ b/Source/WebCore/rendering/RenderViewTransitionCapture.cpp @@ -85,8 +85,8 @@ void RenderViewTransitionCapture::paintReplaced(PaintInfo& paintInfo, const Layo FloatRect paintRect = m_localOverflowRect; InterpolationQualityMaintainer interpolationMaintainer(context, ImageQualityController::interpolationQualityFromStyle(style())); - if (m_oldImage) - context.drawImageBuffer(*m_oldImage, paintRect, { context.compositeOperation() }); + if (RefPtr oldImage = m_oldImage) + context.drawImageBuffer(*oldImage, paintRect, { context.compositeOperation() }); } void RenderViewTransitionCapture::layout() From 1cc5b31dc23a8b0fde32278dbf4dc841ef04d3b0 Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Sun, 23 Feb 2025 11:11:51 -0800 Subject: [PATCH 093/174] MediaStreamTrack backgroundBlur setting is never updated rdar://145280495 https://bugs.webkit.org/show_bug.cgi?id=287692 Reviewed by Eric Carlson. In case of configuration change for video tracks, we need to reset the settings to be able to get the new settings. Updated mock infrastructure to test this. * LayoutTests/fast/mediastream/getDisplayMedia-max-constraints4.html: * LayoutTests/fast/mediastream/getDisplayMedia-max-constraints5.html: * LayoutTests/fast/mediastream/mediastreamtrack-configurationchange-expected.txt: * LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html: * Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp: (WebCore::MockRealtimeMediaSourceCenter::triggerMockCaptureConfigurationChange): * Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h: * Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp: (WebCore::MockRealtimeVideoSource::triggerCameraConfigurationChange): * Source/WebCore/platform/mock/MockRealtimeVideoSource.h: * Source/WebKit/GPUProcess/GPUProcess.cpp: (WebKit::GPUProcess::triggerMockCaptureConfigurationChange): * Source/WebKit/GPUProcess/GPUProcess.h: * Source/WebKit/GPUProcess/GPUProcess.messages.in: * Source/WebKit/UIProcess/API/C/WKPage.cpp: (WKPageTriggerMockCaptureConfigurationChange): * Source/WebKit/UIProcess/API/C/WKPagePrivate.h: * Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp: * Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp: (WebKit::GPUProcessProxy::triggerMockCaptureConfigurationChange): * Source/WebKit/UIProcess/GPU/GPUProcessProxy.h: * Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl: * Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp: (WTR::TestRunner::triggerMockCaptureConfigurationChange): * Tools/WebKitTestRunner/InjectedBundle/TestRunner.h: * Tools/WebKitTestRunner/TestController.cpp: (WTR::TestController::triggerMockCaptureConfigurationChange): * Tools/WebKitTestRunner/TestController.h: * Tools/WebKitTestRunner/TestInvocation.cpp: (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle): Canonical link: https://commits.webkit.org/290914@main --- .../getDisplayMedia-max-constraints4.html | 2 +- .../getDisplayMedia-max-constraints5.html | 2 +- ...reamtrack-configurationchange-expected.txt | 1 + .../mediastreamtrack-configurationchange.html | 26 ++++++++++++++++++- .../mock/MockRealtimeMediaSourceCenter.cpp | 5 +++- .../mock/MockRealtimeMediaSourceCenter.h | 2 +- .../platform/mock/MockRealtimeVideoSource.cpp | 17 ++++++++++++ .../platform/mock/MockRealtimeVideoSource.h | 1 + Source/WebKit/GPUProcess/GPUProcess.cpp | 4 +-- Source/WebKit/GPUProcess/GPUProcess.h | 2 +- .../WebKit/GPUProcess/GPUProcess.messages.in | 2 +- Source/WebKit/UIProcess/API/C/WKPage.cpp | 6 ++--- Source/WebKit/UIProcess/API/C/WKPagePrivate.h | 2 +- .../Cocoa/UserMediaCaptureManagerProxy.cpp | 1 + .../WebKit/UIProcess/GPU/GPUProcessProxy.cpp | 4 +-- Source/WebKit/UIProcess/GPU/GPUProcessProxy.h | 2 +- .../InjectedBundle/Bindings/TestRunner.idl | 2 +- .../InjectedBundle/TestRunner.cpp | 3 ++- .../InjectedBundle/TestRunner.h | 2 +- Tools/WebKitTestRunner/TestController.cpp | 4 +-- Tools/WebKitTestRunner/TestController.h | 2 +- Tools/WebKitTestRunner/TestInvocation.cpp | 3 ++- 22 files changed, 72 insertions(+), 23 deletions(-) diff --git a/LayoutTests/fast/mediastream/getDisplayMedia-max-constraints4.html b/LayoutTests/fast/mediastream/getDisplayMedia-max-constraints4.html index 2d620b7e5ebc0..6920ecb4facc2 100644 --- a/LayoutTests/fast/mediastream/getDisplayMedia-max-constraints4.html +++ b/LayoutTests/fast/mediastream/getDisplayMedia-max-constraints4.html @@ -29,7 +29,7 @@ setTimeout(() => reject("configuration change timeout"), 5000); }); if (window.testRunner) - testRunner.triggerMockCaptureConfigurationChange(false, true); + testRunner.triggerMockCaptureConfigurationChange(false, false, true); await promise; let videoFrame2; diff --git a/LayoutTests/fast/mediastream/getDisplayMedia-max-constraints5.html b/LayoutTests/fast/mediastream/getDisplayMedia-max-constraints5.html index 0ca431853982f..9c704eb2af453 100644 --- a/LayoutTests/fast/mediastream/getDisplayMedia-max-constraints5.html +++ b/LayoutTests/fast/mediastream/getDisplayMedia-max-constraints5.html @@ -38,7 +38,7 @@ }); if (window.testRunner) - testRunner.triggerMockCaptureConfigurationChange(false, true); + testRunner.triggerMockCaptureConfigurationChange(false, false, true); await promise; await validateSize(test, stream, 3000, 1687, "new video original") diff --git a/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange-expected.txt b/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange-expected.txt index 9764691bb5705..59f6d148853f6 100644 --- a/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange-expected.txt +++ b/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange-expected.txt @@ -1,4 +1,5 @@ PASS Trigger configurationchange event in case OS changes microphone on its own +PASS Trigger configurationchange event in case background blur changes diff --git a/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html b/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html index a8cf47676c2de..ba62b1fbe16c3 100644 --- a/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html +++ b/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html @@ -21,7 +21,7 @@ assert_equals(track.label, "Mock audio device 1"); - testRunner.triggerMockCaptureConfigurationChange(true, false); + testRunner.triggerMockCaptureConfigurationChange(false, true, false); await new Promise((resolve, reject) => { track.onconfigurationchange = resolve; @@ -33,6 +33,30 @@ await new Promise(resolve => setTimeout(resolve, 2000)); assert_equals(track.readyState, "live"); }, "Trigger configurationchange event in case OS changes microphone on its own"); + + promise_test(async (t) => { + if (!window.testRunner) + return; + + const stream = await navigator.mediaDevices.getUserMedia({ video: true }); + const track = stream.getVideoTracks()[0]; + + video.srcObject = stream; + await video.play(); + + assert_false(track.getSettings().backgroundBlur); + assert_array_equals(track.getCapabilities().backgroundBlur, [false]); + + testRunner.triggerMockCaptureConfigurationChange(true, false, false); + + await new Promise((resolve, reject) => { + track.onconfigurationchange = resolve; + setTimeout(()=> reject("waited too long for configurationchange"), 5000); + }); + + assert_true(track.getSettings().backgroundBlur); + assert_array_equals(track.getCapabilities().backgroundBlur, [true]); + }, "Trigger configurationchange event in case background blur changes"); diff --git a/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp b/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp index 419f3ca8eb8ac..7c6dc0fa6813b 100644 --- a/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp +++ b/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp @@ -433,8 +433,11 @@ void MockRealtimeMediaSourceCenter::setMockCaptureDevicesInterrupted(bool isCame MockRealtimeAudioSource::setIsInterrupted(isMicrophoneInterrupted); } -void MockRealtimeMediaSourceCenter::triggerMockCaptureConfigurationChange(bool forMicrophone, bool forDisplay) +void MockRealtimeMediaSourceCenter::triggerMockCaptureConfigurationChange(bool forCamera, bool forMicrophone, bool forDisplay) { + if (forCamera) + MockRealtimeVideoSource::triggerCameraConfigurationChange(); + #if PLATFORM(COCOA) if (forMicrophone) { auto devices = audioCaptureDeviceManager().captureDevices(); diff --git a/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h b/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h index bcdf09de9c354..a8454d3a146fb 100644 --- a/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h +++ b/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h @@ -50,7 +50,7 @@ class MockRealtimeMediaSourceCenter { WEBCORE_EXPORT static void resetDevices(); WEBCORE_EXPORT static void setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted); - WEBCORE_EXPORT void triggerMockCaptureConfigurationChange(bool forMicrophone, bool forDisplay); + WEBCORE_EXPORT void triggerMockCaptureConfigurationChange(bool forCamera, bool forMicrophone, bool forDisplay); void setMockAudioCaptureEnabled(bool isEnabled) { m_isMockAudioCaptureEnabled = isEnabled; } void setMockVideoCaptureEnabled(bool isEnabled) { m_isMockVideoCaptureEnabled = isEnabled; } diff --git a/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp b/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp index 001f5a1965e5f..b84bf40445744 100644 --- a/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp +++ b/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp @@ -772,6 +772,23 @@ void MockRealtimeVideoSource::setIsInterrupted(bool isInterrupted) } } +void MockRealtimeVideoSource::triggerCameraConfigurationChange() +{ + for (auto& source : allMockRealtimeVideoSource()) { + if (!source.isProducingData() || source.deviceType() != CaptureDevice::DeviceType::Camera) + continue; + + std::get(source.m_device.properties).hasBackgroundBlur = !std::get(source.m_device.properties).hasBackgroundBlur; + + source.m_currentSettings = { }; + source.m_capabilities = { }; + + source.forEachObserver([](auto& observer) { + observer.sourceConfigurationChanged(); + }); + } +} + void MockRealtimeVideoSource::startApplyingConstraints() { ASSERT(!m_beingConfigured); diff --git a/Source/WebCore/platform/mock/MockRealtimeVideoSource.h b/Source/WebCore/platform/mock/MockRealtimeVideoSource.h index 9c6e264bb4dbe..1178a61373bf5 100644 --- a/Source/WebCore/platform/mock/MockRealtimeVideoSource.h +++ b/Source/WebCore/platform/mock/MockRealtimeVideoSource.h @@ -55,6 +55,7 @@ class MockRealtimeVideoSource : public RealtimeVideoCaptureSource, private Orien virtual ~MockRealtimeVideoSource(); static void setIsInterrupted(bool); + static void triggerCameraConfigurationChange(); ImageBuffer* imageBuffer(); diff --git a/Source/WebKit/GPUProcess/GPUProcess.cpp b/Source/WebKit/GPUProcess/GPUProcess.cpp index 0dea121263139..41ca968041afb 100644 --- a/Source/WebKit/GPUProcess/GPUProcess.cpp +++ b/Source/WebKit/GPUProcess/GPUProcess.cpp @@ -468,9 +468,9 @@ void GPUProcess::setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool WebCore::MockRealtimeMediaSourceCenter::setMockCaptureDevicesInterrupted(isCameraInterrupted, isMicrophoneInterrupted); } -void GPUProcess::triggerMockCaptureConfigurationChange(bool forMicrophone, bool forDisplay) +void GPUProcess::triggerMockCaptureConfigurationChange(bool forCamera, bool forMicrophone, bool forDisplay) { - WebCore::MockRealtimeMediaSourceCenter::singleton().triggerMockCaptureConfigurationChange(forMicrophone, forDisplay); + WebCore::MockRealtimeMediaSourceCenter::singleton().triggerMockCaptureConfigurationChange(forCamera, forMicrophone, forDisplay); } void GPUProcess::setShouldListenToVoiceActivity(bool shouldListen) diff --git a/Source/WebKit/GPUProcess/GPUProcess.h b/Source/WebKit/GPUProcess/GPUProcess.h index 2613ff14c0c22..36d7e5818504a 100644 --- a/Source/WebKit/GPUProcess/GPUProcess.h +++ b/Source/WebKit/GPUProcess/GPUProcess.h @@ -207,7 +207,7 @@ class GPUProcess final : public AuxiliaryProcess, public ThreadSafeRefCounted GPUProcess : AuxiliaryProcess { SetMockMediaDeviceIsEphemeral(String persistentId, bool isEphemeral) ResetMockMediaDevices() SetMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted) - TriggerMockCaptureConfigurationChange(bool forMicrophone, bool forDisplay) + TriggerMockCaptureConfigurationChange(bool forCamera, bool forMicrophone, bool forDisplay) SetShouldListenToVoiceActivity(bool shouldListen) EnableMicrophoneMuteStatusAPI() #endif diff --git a/Source/WebKit/UIProcess/API/C/WKPage.cpp b/Source/WebKit/UIProcess/API/C/WKPage.cpp index 261d3064b6cdc..2e9502e5767eb 100644 --- a/Source/WebKit/UIProcess/API/C/WKPage.cpp +++ b/Source/WebKit/UIProcess/API/C/WKPage.cpp @@ -3373,11 +3373,11 @@ void WKPageSetMockCaptureDevicesInterrupted(WKPageRef pageRef, bool isCameraInte #endif } -void WKPageTriggerMockCaptureConfigurationChange(WKPageRef pageRef, bool forMicrophone, bool forDisplay) +void WKPageTriggerMockCaptureConfigurationChange(WKPageRef pageRef, bool forCamera, bool forMicrophone, bool forDisplay) { CRASH_IF_SUSPENDED; #if ENABLE(MEDIA_STREAM) - MockRealtimeMediaSourceCenter::singleton().triggerMockCaptureConfigurationChange(forMicrophone, forDisplay); + MockRealtimeMediaSourceCenter::singleton().triggerMockCaptureConfigurationChange(forCamera, forMicrophone, forDisplay); #if ENABLE(GPU_PROCESS) auto preferences = toImpl(pageRef)->protectedPreferences(); @@ -3385,7 +3385,7 @@ void WKPageTriggerMockCaptureConfigurationChange(WKPageRef pageRef, bool forMicr return; auto& gpuProcess = toImpl(pageRef)->configuration().processPool().ensureGPUProcess(); - gpuProcess.triggerMockCaptureConfigurationChange(forMicrophone, forDisplay); + gpuProcess.triggerMockCaptureConfigurationChange(forCamera, forMicrophone, forDisplay); #endif // ENABLE(GPU_PROCESS) #endif // ENABLE(MEDIA_STREAM) diff --git a/Source/WebKit/UIProcess/API/C/WKPagePrivate.h b/Source/WebKit/UIProcess/API/C/WKPagePrivate.h index 2e88e31c04429..baa9438e07137 100644 --- a/Source/WebKit/UIProcess/API/C/WKPagePrivate.h +++ b/Source/WebKit/UIProcess/API/C/WKPagePrivate.h @@ -210,7 +210,7 @@ WK_EXPORT void WKPageSetPrivateClickMeasurementAppBundleIDForTesting(WKPageRef p WK_EXPORT void WKPageSetMockCameraOrientationForTesting(WKPageRef page, uint64_t rotation, WKStringRef persistentId); WK_EXPORT bool WKPageIsMockRealtimeMediaSourceCenterEnabled(WKPageRef page); WK_EXPORT void WKPageSetMockCaptureDevicesInterrupted(WKPageRef page, bool isCameraInterrupted, bool isMicrophoneInterrupted); -WK_EXPORT void WKPageTriggerMockCaptureConfigurationChange(WKPageRef page, bool forMicrophone, bool forDisplay); +WK_EXPORT void WKPageTriggerMockCaptureConfigurationChange(WKPageRef page, bool forCamera, bool forMicrophone, bool forDisplay); typedef void (*WKPageLoadedSubresourceDomainsFunction)(WKArrayRef domains, void* functionContext); WK_EXPORT void WKPageLoadedSubresourceDomains(WKPageRef page, WKPageLoadedSubresourceDomainsFunction callback, void* callbackContext); diff --git a/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp b/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp index 015295accc1f2..0e6845da33ffb 100644 --- a/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp +++ b/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp @@ -337,6 +337,7 @@ class UserMediaCaptureManagerProxySourceProxy final source->addVideoFrameObserver(*this, { m_widthConstraint, m_heightConstraint }, m_frameRateConstraint); } + m_settings = { }; protectedConnection()->send(Messages::UserMediaCaptureManager::SourceConfigurationChanged(m_id, source->persistentID(), settings(), source->capabilities()), 0); } diff --git a/Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp b/Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp index e5684f545c317..dc8bebfee5421 100644 --- a/Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp +++ b/Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp @@ -467,9 +467,9 @@ void GPUProcessProxy::setMockCaptureDevicesInterrupted(bool isCameraInterrupted, send(Messages::GPUProcess::SetMockCaptureDevicesInterrupted { isCameraInterrupted, isMicrophoneInterrupted }, 0); } -void GPUProcessProxy::triggerMockCaptureConfigurationChange(bool forMicrophone, bool forDisplay) +void GPUProcessProxy::triggerMockCaptureConfigurationChange(bool forCamera, bool forMicrophone, bool forDisplay) { - send(Messages::GPUProcess::TriggerMockCaptureConfigurationChange { forMicrophone, forDisplay }, 0); + send(Messages::GPUProcess::TriggerMockCaptureConfigurationChange { forCamera, forMicrophone, forDisplay }, 0); } void GPUProcessProxy::setShouldListenToVoiceActivity(const WebPageProxy& proxy, bool shouldListen) diff --git a/Source/WebKit/UIProcess/GPU/GPUProcessProxy.h b/Source/WebKit/UIProcess/GPU/GPUProcessProxy.h index 39051cccbf92e..cd17f48052cc1 100644 --- a/Source/WebKit/UIProcess/GPU/GPUProcessProxy.h +++ b/Source/WebKit/UIProcess/GPU/GPUProcessProxy.h @@ -111,7 +111,7 @@ class GPUProcessProxy final : public AuxiliaryProcessProxy { void setMockMediaDeviceIsEphemeral(const String&, bool); void resetMockMediaDevices(); void setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted); - void triggerMockCaptureConfigurationChange(bool forMicrophone, bool forDisplay); + void triggerMockCaptureConfigurationChange(bool forCamera, bool forMicrophone, bool forDisplay); void updateSandboxAccess(bool allowAudioCapture, bool allowVideoCapture, bool allowDisplayCapture); void setShouldListenToVoiceActivity(const WebPageProxy&, bool); void setPageUsingMicrophone(WebPageProxyIdentifier identifier) { m_lastPageUsingMicrophone = identifier; } diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl index 8feff83cffea6..d44566faf05cc 100644 --- a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl +++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl @@ -408,7 +408,7 @@ interface TestRunner { undefined setMockCameraOrientation(unsigned long orientation, DOMString persistentId); boolean isMockRealtimeMediaSourceCenterEnabled(); undefined setMockCaptureDevicesInterrupted(boolean isCameraInterrupted, boolean isMicrophoneInterrupted); - undefined triggerMockCaptureConfigurationChange(boolean forMicrophone, boolean forDisplay); + undefined triggerMockCaptureConfigurationChange(boolean forCamera, boolean forMicrophone, boolean forDisplay); undefined setCaptureState(boolean cameraState, boolean microphoneState, boolean displayState); diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp index 35e737318a11d..46ad295e8b208 100644 --- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp +++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp @@ -1671,9 +1671,10 @@ void TestRunner::setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool })); } -void TestRunner::triggerMockCaptureConfigurationChange(bool forMicrophone, bool forDisplay) +void TestRunner::triggerMockCaptureConfigurationChange(bool forCamera, bool forMicrophone, bool forDisplay) { postSynchronousMessage("TriggerMockCaptureConfigurationChange", createWKDictionary({ + { "camera", adoptWK(WKBooleanCreate(forCamera)) }, { "microphone", adoptWK(WKBooleanCreate(forMicrophone)) }, { "display", adoptWK(WKBooleanCreate(forDisplay)) }, })); diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h index 855aca9aec7d4..86ef6dcb9e170 100644 --- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h +++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h @@ -516,7 +516,7 @@ class TestRunner : public JSWrappable { void setMockCameraOrientation(unsigned, JSStringRef persistentId); bool isMockRealtimeMediaSourceCenterEnabled(); void setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted); - void triggerMockCaptureConfigurationChange(bool forMicrophone, bool forDisplay); + void triggerMockCaptureConfigurationChange(bool forCamera, bool forMicrophone, bool forDisplay); void setCaptureState(bool cameraState, bool microphoneState, bool displayState); bool hasAppBoundSession(); diff --git a/Tools/WebKitTestRunner/TestController.cpp b/Tools/WebKitTestRunner/TestController.cpp index cb6f4d5477ac9..ea8a7dc0d6b63 100644 --- a/Tools/WebKitTestRunner/TestController.cpp +++ b/Tools/WebKitTestRunner/TestController.cpp @@ -4315,9 +4315,9 @@ void TestController::setMockCaptureDevicesInterrupted(bool isCameraInterrupted, WKPageSetMockCaptureDevicesInterrupted(m_mainWebView->page(), isCameraInterrupted, isMicrophoneInterrupted); } -void TestController::triggerMockCaptureConfigurationChange(bool forMicrophone, bool forDisplay) +void TestController::triggerMockCaptureConfigurationChange(bool forCamera, bool forMicrophone, bool forDisplay) { - WKPageTriggerMockCaptureConfigurationChange(m_mainWebView->page(), forMicrophone, forDisplay); + WKPageTriggerMockCaptureConfigurationChange(m_mainWebView->page(), forCamera, forMicrophone, forDisplay); } void TestController::setCaptureState(bool cameraState, bool microphoneState, bool displayState) diff --git a/Tools/WebKitTestRunner/TestController.h b/Tools/WebKitTestRunner/TestController.h index e7c49dfe632a5..c865514494d1e 100644 --- a/Tools/WebKitTestRunner/TestController.h +++ b/Tools/WebKitTestRunner/TestController.h @@ -360,7 +360,7 @@ class TestController { void setMockCameraOrientation(uint64_t, WKStringRef); bool isMockRealtimeMediaSourceCenterEnabled() const; void setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted); - void triggerMockCaptureConfigurationChange(bool forMicrophone, bool forDisplay); + void triggerMockCaptureConfigurationChange(bool forCamera, bool forMicrophone, bool forDisplay); void setCaptureState(bool cameraState, bool microphoneState, bool displayState); bool hasAppBoundSession(); diff --git a/Tools/WebKitTestRunner/TestInvocation.cpp b/Tools/WebKitTestRunner/TestInvocation.cpp index f8d8195a26f43..4da33a0d1ac46 100644 --- a/Tools/WebKitTestRunner/TestInvocation.cpp +++ b/Tools/WebKitTestRunner/TestInvocation.cpp @@ -925,9 +925,10 @@ WKRetainPtr TestInvocation::didReceiveSynchronousMessageFromInjectedB if (WKStringIsEqualToUTF8CString(messageName, "TriggerMockCaptureConfigurationChange")) { auto messageBodyDictionary = dictionaryValue(messageBody); + bool forCamera = booleanValue(messageBodyDictionary, "camera"); bool forMicrophone = booleanValue(messageBodyDictionary, "microphone"); bool forDisplay = booleanValue(messageBodyDictionary, "display"); - TestController::singleton().triggerMockCaptureConfigurationChange(forMicrophone, forDisplay); + TestController::singleton().triggerMockCaptureConfigurationChange(forCamera, forMicrophone, forDisplay); return nullptr; } From daadfcfb7025c547ebeff695223e0fdaa41c6101 Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Sun, 23 Feb 2025 11:13:11 -0800 Subject: [PATCH 094/174] Abort FileSystem writables in case of network process crash or storage clearing https://bugs.webkit.org/show_bug.cgi?id=287235 rdar://problem/144380830 Reviewed by Sihui Liu. When a writable is open, it may be aborted in case the network process crashes or in case the underlying file is deleted. To support this mechanism, we allow networking process to invalidate a writable like done for sync access handles. Similarly, if a network process crashes, we add the same mechanism for writables as for sync access handles. When a writable (aka FileSystemWritableFileStream) gets crated, we register it in its FileSystemStorageConnection. When a writable is closed, we unregister it as well. When a FileSystemStorageConnection is instructed to error a writable, it will call the new WritableStream errorIfPossible method. Covered by added test. * LayoutTests/storage/filesystemaccess/writable-file-stream-abort-expected.txt: Added. * LayoutTests/storage/filesystemaccess/writable-file-stream-abort.html: Added. * Source/WebCore/Modules/filesystemaccess/FileSystemFileHandle.cpp: (WebCore::FileSystemFileHandle::createWritable): (WebCore::FileSystemFileHandle::closeWritable): * Source/WebCore/Modules/filesystemaccess/FileSystemStorageConnection.cpp: Added. (WebCore::FileSystemStorageConnection::errorFileSystemWritable): (WebCore::FileSystemStorageConnection::registerFileSystemWritable): (WebCore::FileSystemStorageConnection::unregisterFileSystemWritable): * Source/WebCore/Modules/filesystemaccess/FileSystemStorageConnection.h: * Source/WebCore/Modules/filesystemaccess/WorkerFileSystemStorageConnection.cpp: (WebCore::WorkerFileSystemStorageConnection::createWritable): * Source/WebCore/Modules/filesystemaccess/WorkerFileSystemStorageConnection.h: * Source/WebCore/Modules/streams/WritableStream.cpp: (WebCore::WritableStream::errorIfPossible): * Source/WebCore/Modules/streams/WritableStream.h: * Source/WebCore/Modules/streams/WritableStreamInternals.js: (writableStreamErrorIfPossible): * Source/WebCore/Sources.txt: * Source/WebCore/WebCore.xcodeproj/project.pbxproj: * Source/WebCore/bindings/js/InternalWritableStream.cpp: (WebCore::InternalWritableStream::errorIfPossible): * Source/WebCore/bindings/js/InternalWritableStream.h: * Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.cpp: (WebKit::FileSystemStorageHandle::writables const): * Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.h: * Source/WebKit/NetworkProcess/storage/FileSystemStorageManager.cpp: (WebKit::FileSystemStorageManager::close): * Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.cpp: (WebKit::WebFileSystemStorageConnection::errorWritable): (WebKit::WebFileSystemStorageConnection::connectionClosed): (WebKit::WebFileSystemStorageConnection::createWritable): (WebKit::WebFileSystemStorageConnection::invalidateWritable): (WebKit::WebFileSystemStorageConnection::closeWritable): * Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.h: * Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.messages.in: * Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl: * Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp: (WTR::TestRunner::clearStorage): * Tools/WebKitTestRunner/InjectedBundle/TestRunner.h: * Tools/WebKitTestRunner/TestInvocation.cpp: (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle): Canonical link: https://commits.webkit.org/290915@main --- .../writable-file-stream-abort-expected.txt | 6 ++ .../writable-file-stream-abort.html | 81 +++++++++++++++++++ .../filesystemaccess/FileSystemFileHandle.cpp | 5 +- .../FileSystemStorageConnection.cpp | 52 ++++++++++++ .../FileSystemStorageConnection.h | 11 ++- .../WorkerFileSystemStorageConnection.cpp | 6 +- .../WorkerFileSystemStorageConnection.h | 2 +- .../Modules/streams/WritableStream.cpp | 5 ++ .../WebCore/Modules/streams/WritableStream.h | 1 + .../streams/WritableStreamInternals.js | 10 +++ Source/WebCore/Sources.txt | 1 + .../WebCore/WebCore.xcodeproj/project.pbxproj | 2 + .../bindings/js/InternalWritableStream.cpp | 25 ++++++ .../bindings/js/InternalWritableStream.h | 1 + .../storage/FileSystemStorageHandle.cpp | 5 ++ .../storage/FileSystemStorageHandle.h | 1 + .../storage/FileSystemStorageManager.cpp | 5 +- .../WebFileSystemStorageConnection.cpp | 30 ++++++- .../WebFileSystemStorageConnection.h | 6 +- ...WebFileSystemStorageConnection.messages.in | 1 + .../InjectedBundle/Bindings/TestRunner.idl | 2 + .../InjectedBundle/TestRunner.cpp | 5 ++ .../InjectedBundle/TestRunner.h | 2 + Tools/WebKitTestRunner/TestInvocation.cpp | 5 ++ 24 files changed, 260 insertions(+), 10 deletions(-) create mode 100644 LayoutTests/storage/filesystemaccess/writable-file-stream-abort-expected.txt create mode 100644 LayoutTests/storage/filesystemaccess/writable-file-stream-abort.html create mode 100644 Source/WebCore/Modules/filesystemaccess/FileSystemStorageConnection.cpp diff --git a/LayoutTests/storage/filesystemaccess/writable-file-stream-abort-expected.txt b/LayoutTests/storage/filesystemaccess/writable-file-stream-abort-expected.txt new file mode 100644 index 0000000000000..6c3b57e530442 --- /dev/null +++ b/LayoutTests/storage/filesystemaccess/writable-file-stream-abort-expected.txt @@ -0,0 +1,6 @@ + +PASS Validate stream gets errored in case of network process crash +PASS Validate stream gets errored in case of clearing storage +PASS Validate stream gets errored in case of network process crash in a worker +PASS Validate stream gets errored in case of clearing storage in a worker + diff --git a/LayoutTests/storage/filesystemaccess/writable-file-stream-abort.html b/LayoutTests/storage/filesystemaccess/writable-file-stream-abort.html new file mode 100644 index 0000000000000..b2f8157b82fbc --- /dev/null +++ b/LayoutTests/storage/filesystemaccess/writable-file-stream-abort.html @@ -0,0 +1,81 @@ + + + + + + + + + diff --git a/Source/WebCore/Modules/filesystemaccess/FileSystemFileHandle.cpp b/Source/WebCore/Modules/filesystemaccess/FileSystemFileHandle.cpp index 7da35a5a9194e..63592a497577f 100644 --- a/Source/WebCore/Modules/filesystemaccess/FileSystemFileHandle.cpp +++ b/Source/WebCore/Modules/filesystemaccess/FileSystemFileHandle.cpp @@ -134,7 +134,7 @@ void FileSystemFileHandle::createWritable(const CreateWritableOptions& options, if (isClosed()) return promise.reject(Exception { ExceptionCode::InvalidStateError, "Handle is closed"_s }); - connection().createWritable(identifier(), options.keepExistingData, [this, protectedThis = Ref { *this }, promise = WTFMove(promise)](auto result) mutable { + connection().createWritable(scriptExecutionContext()->identifier(), identifier(), options.keepExistingData, [this, protectedThis = Ref { *this }, promise = WTFMove(promise)](auto result) mutable { if (result.hasException()) return promise.reject(result.releaseException()); @@ -163,6 +163,8 @@ void FileSystemFileHandle::createWritable(const CreateWritableOptions& options, Locker locker(globalObject->vm().apiLock()); stream = FileSystemWritableFileStream::create(*globalObject, sink.releaseReturnValue()); } + if (!stream.hasException()) + connection().registerFileSystemWritable(streamIdentifier, stream.returnValue()); promise.settle(WTFMove(stream)); }); @@ -170,6 +172,7 @@ void FileSystemFileHandle::createWritable(const CreateWritableOptions& options, void FileSystemFileHandle::closeWritable(FileSystemWritableFileStreamIdentifier streamIdentifier, FileSystemWriteCloseReason reason) { + connection().unregisterFileSystemWritable(streamIdentifier); if (!isClosed()) connection().closeWritable(identifier(), streamIdentifier, reason, [](auto) { }); } diff --git a/Source/WebCore/Modules/filesystemaccess/FileSystemStorageConnection.cpp b/Source/WebCore/Modules/filesystemaccess/FileSystemStorageConnection.cpp new file mode 100644 index 0000000000000..31c308aa01813 --- /dev/null +++ b/Source/WebCore/Modules/filesystemaccess/FileSystemStorageConnection.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "config.h" +#include "FileSystemStorageConnection.h" + +namespace WebCore { + +bool FileSystemStorageConnection::errorFileSystemWritable(FileSystemWritableFileStreamIdentifier identifier) +{ + RefPtr writable = m_writables.take(identifier).get(); + if (writable) + writable->errorIfPossible(Exception { ExceptionCode::AbortError }); + return writable; +} + +void FileSystemStorageConnection::registerFileSystemWritable(FileSystemWritableFileStreamIdentifier identifier, FileSystemWritableFileStream& writer) +{ + ASSERT(!m_writables.contains(identifier)); + m_writables.add(identifier, WeakPtr { writer }); +} + +void FileSystemStorageConnection::unregisterFileSystemWritable(FileSystemWritableFileStreamIdentifier identifier) +{ + m_writables.remove(identifier); +} + +} // namespace WebCore diff --git a/Source/WebCore/Modules/filesystemaccess/FileSystemStorageConnection.h b/Source/WebCore/Modules/filesystemaccess/FileSystemStorageConnection.h index c7e546420b75c..580b98f97c19f 100644 --- a/Source/WebCore/Modules/filesystemaccess/FileSystemStorageConnection.h +++ b/Source/WebCore/Modules/filesystemaccess/FileSystemStorageConnection.h @@ -34,6 +34,7 @@ #include "ProcessQualified.h" #include "ScriptExecutionContextIdentifier.h" #include +#include #include namespace WebCore { @@ -43,6 +44,7 @@ class FileSystemFileHandle; class FileHandle; class FileSystemHandleCloseScope; class FileSystemSyncAccessHandle; +class FileSystemWritableFileStream; template class ExceptionOr; class FileSystemStorageConnection : public ThreadSafeRefCounted { @@ -81,11 +83,18 @@ class FileSystemStorageConnection : public ThreadSafeRefCounted position, std::optional size, std::span dataBytes, bool hasDataError, VoidCallback&&) = 0; virtual void getHandleNames(FileSystemHandleIdentifier, GetHandleNamesCallback&&) = 0; virtual void getHandle(FileSystemHandleIdentifier, const String& name, GetHandleCallback&&) = 0; + + WEBCORE_EXPORT bool errorFileSystemWritable(FileSystemWritableFileStreamIdentifier); + void registerFileSystemWritable(FileSystemWritableFileStreamIdentifier, FileSystemWritableFileStream&); + void unregisterFileSystemWritable(FileSystemWritableFileStreamIdentifier); + +private: + HashMap> m_writables; }; } // namespace WebCore diff --git a/Source/WebCore/Modules/filesystemaccess/WorkerFileSystemStorageConnection.cpp b/Source/WebCore/Modules/filesystemaccess/WorkerFileSystemStorageConnection.cpp index 0e29416913483..837dffc70a9a9 100644 --- a/Source/WebCore/Modules/filesystemaccess/WorkerFileSystemStorageConnection.cpp +++ b/Source/WebCore/Modules/filesystemaccess/WorkerFileSystemStorageConnection.cpp @@ -317,7 +317,7 @@ void WorkerFileSystemStorageConnection::invalidateAccessHandle(WebCore::FileSyst handle->invalidate(); } -void WorkerFileSystemStorageConnection::createWritable(FileSystemHandleIdentifier identifier, bool keepExistingData, StreamCallback&& callback) +void WorkerFileSystemStorageConnection::createWritable(ScriptExecutionContextIdentifier contextIdentifier, FileSystemHandleIdentifier identifier, bool keepExistingData, StreamCallback&& callback) { if (!m_scope) return callback(Exception { ExceptionCode::InvalidStateError }); @@ -325,7 +325,7 @@ void WorkerFileSystemStorageConnection::createWritable(FileSystemHandleIdentifie auto callbackIdentifier = CallbackIdentifier::generate(); m_streamCallbacks.add(callbackIdentifier, WTFMove(callback)); - callOnMainThread([callbackIdentifier, workerThread = Ref { m_scope->thread() }, mainThreadConnection = m_mainThreadConnection, identifier, keepExistingData]() mutable { + callOnMainThread([callbackIdentifier, workerThread = Ref { m_scope->thread() }, mainThreadConnection = m_mainThreadConnection, contextIdentifier, identifier, keepExistingData]() mutable { auto mainThreadCallback = [callbackIdentifier, workerThread = WTFMove(workerThread)](auto&& result) mutable { workerThread->runLoop().postTaskForMode([callbackIdentifier, result = crossThreadCopy(WTFMove(result))] (auto& scope) mutable { if (RefPtr connection = downcast(scope).fileSystemStorageConnection()) { @@ -335,7 +335,7 @@ void WorkerFileSystemStorageConnection::createWritable(FileSystemHandleIdentifie }, WorkerRunLoop::defaultMode()); }; - mainThreadConnection->createWritable(identifier, keepExistingData, WTFMove(mainThreadCallback)); + mainThreadConnection->createWritable(contextIdentifier, identifier, keepExistingData, WTFMove(mainThreadCallback)); }); } diff --git a/Source/WebCore/Modules/filesystemaccess/WorkerFileSystemStorageConnection.h b/Source/WebCore/Modules/filesystemaccess/WorkerFileSystemStorageConnection.h index a40184f045898..3e2ce3e09f7d1 100644 --- a/Source/WebCore/Modules/filesystemaccess/WorkerFileSystemStorageConnection.h +++ b/Source/WebCore/Modules/filesystemaccess/WorkerFileSystemStorageConnection.h @@ -79,7 +79,7 @@ class WorkerFileSystemStorageConnection final : public FileSystemStorageConnecti void unregisterSyncAccessHandle(FileSystemSyncAccessHandleIdentifier) final; void invalidateAccessHandle(FileSystemSyncAccessHandleIdentifier) final; void requestNewCapacityForSyncAccessHandle(FileSystemHandleIdentifier, FileSystemSyncAccessHandleIdentifier, uint64_t, RequestCapacityCallback&&) final; - void createWritable(FileSystemHandleIdentifier, bool keepExistingData, StreamCallback&&) final; + void createWritable(ScriptExecutionContextIdentifier, FileSystemHandleIdentifier, bool keepExistingData, StreamCallback&&) final; void closeWritable(FileSystemHandleIdentifier, FileSystemWritableFileStreamIdentifier, FileSystemWriteCloseReason, VoidCallback&&) final; void executeCommandForWritable(FileSystemHandleIdentifier, FileSystemWritableFileStreamIdentifier, FileSystemWriteCommandType, std::optional position, std::optional size, std::span dataBytes, bool hasDataError, VoidCallback&&) final; diff --git a/Source/WebCore/Modules/streams/WritableStream.cpp b/Source/WebCore/Modules/streams/WritableStream.cpp index c9fd3ac00c690..30bd779ebeb95 100644 --- a/Source/WebCore/Modules/streams/WritableStream.cpp +++ b/Source/WebCore/Modules/streams/WritableStream.cpp @@ -97,6 +97,11 @@ void WritableStream::closeIfPossible() m_internalWritableStream->closeIfPossible(); } +void WritableStream::errorIfPossible(Exception&& e) +{ + m_internalWritableStream->errorIfPossible(WTFMove(e)); +} + JSC::JSValue JSWritableStream::abort(JSC::JSGlobalObject& globalObject, JSC::CallFrame& callFrame) { return wrapped().internalWritableStream().abortForBindings(globalObject, callFrame.argument(0)); diff --git a/Source/WebCore/Modules/streams/WritableStream.h b/Source/WebCore/Modules/streams/WritableStream.h index 9af363e537782..aa2818ca29d64 100644 --- a/Source/WebCore/Modules/streams/WritableStream.h +++ b/Source/WebCore/Modules/streams/WritableStream.h @@ -49,6 +49,7 @@ class WritableStream : public RefCountedAndCanMakeWeakPtr { bool locked() const; void closeIfPossible(); + void errorIfPossible(Exception&&); InternalWritableStream& internalWritableStream(); enum class Type : bool { diff --git a/Source/WebCore/Modules/streams/WritableStreamInternals.js b/Source/WebCore/Modules/streams/WritableStreamInternals.js index dbbdc77fb8842..e21c6fe0c8c7d 100644 --- a/Source/WebCore/Modules/streams/WritableStreamInternals.js +++ b/Source/WebCore/Modules/streams/WritableStreamInternals.js @@ -208,6 +208,16 @@ function writableStreamAbort(stream, reason) return abortPromiseCapability.promise; } +function writableStreamErrorIfPossible(stream, reason) +{ + const state = @getByIdDirectPrivate(stream, "state"); + if (state !== "writable") + return; + + const controller = @getByIdDirectPrivate(stream, "controller"); + @writableStreamDefaultControllerError(controller, reason); +} + function writableStreamCloseIfPossible(stream) { const state = @getByIdDirectPrivate(stream, "state"); diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt index 414b4b0a11e4b..4b56e11842aac 100644 --- a/Source/WebCore/Sources.txt +++ b/Source/WebCore/Sources.txt @@ -140,6 +140,7 @@ Modules/fetch/WindowOrWorkerGlobalScopeFetch.cpp Modules/filesystemaccess/FileSystemDirectoryHandle.cpp Modules/filesystemaccess/FileSystemFileHandle.cpp Modules/filesystemaccess/FileSystemHandle.cpp +Modules/filesystemaccess/FileSystemStorageConnection.cpp Modules/filesystemaccess/FileSystemSyncAccessHandle.cpp Modules/filesystemaccess/FileSystemWritableFileStream.cpp Modules/filesystemaccess/FileSystemWritableFileStreamSink.cpp diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj index 57711ce80a0f7..1d0061a57547c 100644 --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj @@ -10470,6 +10470,7 @@ 41AF37921F8DA48A00111C31 /* ExtendableEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExtendableEvent.cpp; sourceTree = ""; }; 41AF37941F8DA49500111C31 /* FetchEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FetchEvent.cpp; sourceTree = ""; }; 41AF379C1F8DB1B100111C31 /* JSDOMPromise.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDOMPromise.cpp; sourceTree = ""; }; + 41B109772D554D99008A11E8 /* FileSystemStorageConnection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FileSystemStorageConnection.cpp; sourceTree = ""; }; 41B28B121F8501A300FB52AC /* MediaEndpointConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaEndpointConfiguration.h; sourceTree = ""; }; 41B28B131F8501A400FB52AC /* MediaEndpointConfiguration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MediaEndpointConfiguration.cpp; sourceTree = ""; }; 41B28B361F860BD000FB52AC /* LibWebRTCProviderCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LibWebRTCProviderCocoa.h; path = libwebrtc/LibWebRTCProviderCocoa.h; sourceTree = ""; }; @@ -30043,6 +30044,7 @@ 932C9BD926DD62600053B3DB /* FileSystemHandle.idl */, 93445082276A6356001F712B /* FileSystemHandleCloseScope.h */, 935424272703BC88005CA72C /* FileSystemHandleIdentifier.h */, + 41B109772D554D99008A11E8 /* FileSystemStorageConnection.cpp */, 935424292703BCAD005CA72C /* FileSystemStorageConnection.h */, 93E269A0270A5D5C002C4FF0 /* FileSystemSyncAccessHandle.cpp */, 93E2699F270A5D5C002C4FF0 /* FileSystemSyncAccessHandle.h */, diff --git a/Source/WebCore/bindings/js/InternalWritableStream.cpp b/Source/WebCore/bindings/js/InternalWritableStream.cpp index f8741c3280373..10f6e62343017 100644 --- a/Source/WebCore/bindings/js/InternalWritableStream.cpp +++ b/Source/WebCore/bindings/js/InternalWritableStream.cpp @@ -201,6 +201,31 @@ void InternalWritableStream::closeIfPossible() scope.clearException(); } +void InternalWritableStream::errorIfPossible(Exception&& exception) +{ + auto* globalObject = this->globalObject(); + if (!globalObject) + return; + + Ref vm = globalObject->vm(); + JSC::JSLockHolder lock(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + + auto* clientData = downcast(vm->clientData); + auto& privateName = clientData->builtinFunctions().writableStreamInternalsBuiltins().writableStreamErrorIfPossiblePrivateName(); + + auto reason = createDOMException(*globalObject, WTFMove(exception)); + + JSC::MarkedArgumentBuffer arguments; + arguments.append(guardedObject()); + arguments.append(reason); + ASSERT(!arguments.hasOverflowed()); + + invokeWritableStreamFunction(*globalObject, privateName, arguments); + if (UNLIKELY(scope.exception())) + scope.clearException(); +} + JSC::JSValue InternalWritableStream::getWriter(JSC::JSGlobalObject& globalObject) { auto* clientData = downcast(globalObject.vm().clientData); diff --git a/Source/WebCore/bindings/js/InternalWritableStream.h b/Source/WebCore/bindings/js/InternalWritableStream.h index 8185ef1c95d04..fe837bcaf61e9 100644 --- a/Source/WebCore/bindings/js/InternalWritableStream.h +++ b/Source/WebCore/bindings/js/InternalWritableStream.h @@ -45,6 +45,7 @@ class InternalWritableStream final : public DOMGuarded { JSC::JSValue getWriter(JSC::JSGlobalObject&); void closeIfPossible(); + void errorIfPossible(Exception&&); private: InternalWritableStream(JSDOMGlobalObject& globalObject, JSC::JSObject& jsObject) diff --git a/Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.cpp b/Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.cpp index 3775dacfb9e7b..0cc257ba8ed86 100644 --- a/Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.cpp +++ b/Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.cpp @@ -355,6 +355,11 @@ std::optional FileSystemStorageHandle::executeCommandFor return error; } +Vector FileSystemStorageHandle::writables() const +{ + return copyToVector(m_activeWritableFiles.keys()); +} + Expected, FileSystemStorageError> FileSystemStorageHandle::getHandleNames() { if (m_type != Type::Directory) diff --git a/Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.h b/Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.h index 7d74cb670085f..a831ef2d05d8d 100644 --- a/Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.h +++ b/Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.h @@ -77,6 +77,7 @@ class FileSystemStorageHandle final : public RefCounted Expected createWritable(bool keepExistingData); std::optional closeWritable(WebCore::FileSystemWritableFileStreamIdentifier, WebCore::FileSystemWriteCloseReason); std::optional executeCommandForWritable(WebCore::FileSystemWritableFileStreamIdentifier, WebCore::FileSystemWriteCommandType, std::optional position, std::optional size, std::span dataBytes, bool hasDataError); + Vector writables() const; private: FileSystemStorageHandle(FileSystemStorageManager&, Type, String&& path, String&& name); diff --git a/Source/WebKit/NetworkProcess/storage/FileSystemStorageManager.cpp b/Source/WebKit/NetworkProcess/storage/FileSystemStorageManager.cpp index 2b82a46318bde..e0c04db7530ff 100644 --- a/Source/WebKit/NetworkProcess/storage/FileSystemStorageManager.cpp +++ b/Source/WebKit/NetworkProcess/storage/FileSystemStorageManager.cpp @@ -216,9 +216,12 @@ void FileSystemStorageManager::close() if (RefPtr registry = m_registry.get()) registry->unregisterHandle(identifier); - // Send message to web process to invalidate active sync access handle. + // Send messages to web process to invalidate active sync access handle and writables. if (auto accessHandleIdentifier = takenHandle->activeSyncAccessHandle()) IPC::Connection::send(connectionID, Messages::WebFileSystemStorageConnection::InvalidateAccessHandle(*accessHandleIdentifier), 0); + + for (auto writableIdentifier : takenHandle->writables()) + IPC::Connection::send(connectionID, Messages::WebFileSystemStorageConnection::InvalidateWritable(writableIdentifier), 0); } } diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.cpp b/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.cpp index 208def6a6dbf2..bde9708fe368d 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.cpp +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.cpp @@ -47,12 +47,29 @@ WebFileSystemStorageConnection::WebFileSystemStorageConnection(Ref(context); + RefPtr connection = globalScope ? globalScope->fileSystemStorageConnection() : nullptr; + if (connection) + connection->errorFileSystemWritable(writableIdentifier); + }); +} + void WebFileSystemStorageConnection::connectionClosed() { m_connection = nullptr; for (auto identifier : m_syncAccessHandles.keys()) invalidateAccessHandle(identifier); + + auto writableIdentifiers = std::exchange(m_writableIdentifiers, { }); + for (auto keyValue : writableIdentifiers) + errorWritable(keyValue.value, keyValue.key); } void WebFileSystemStorageConnection::closeHandle(WebCore::FileSystemHandleIdentifier identifier) @@ -214,26 +231,35 @@ void WebFileSystemStorageConnection::invalidateAccessHandle(WebCore::FileSystemS } } -void WebFileSystemStorageConnection::createWritable(WebCore::FileSystemHandleIdentifier identifier, bool keepExistingData, StreamCallback&& completionHandler) +void WebFileSystemStorageConnection::createWritable(WebCore::ScriptExecutionContextIdentifier contextIdentifier, WebCore::FileSystemHandleIdentifier identifier, bool keepExistingData, StreamCallback&& completionHandler) { RefPtr connection = m_connection; if (!connection) return completionHandler(WebCore::Exception { WebCore::ExceptionCode::UnknownError, "Connection is lost"_s }); - connection->sendWithAsyncReply(Messages::NetworkStorageManager::CreateWritable(identifier, keepExistingData), [completionHandler = WTFMove(completionHandler)](auto&& result) mutable { + connection->sendWithAsyncReply(Messages::NetworkStorageManager::CreateWritable(identifier, keepExistingData), [protectedThis = Ref { *this }, contextIdentifier, completionHandler = WTFMove(completionHandler)](auto&& result) mutable { if (!result) return completionHandler(convertToException(result.error())); + ASSERT(!protectedThis->m_writableIdentifiers.contains(result.value())); + protectedThis->m_writableIdentifiers.add(result.value(), contextIdentifier); completionHandler(WTFMove(result.value())); }); } +void WebFileSystemStorageConnection::invalidateWritable(WebCore::FileSystemWritableFileStreamIdentifier identifier) +{ + if (auto contextIdentifier = m_writableIdentifiers.take(identifier)) + errorWritable(contextIdentifier, identifier); +} + void WebFileSystemStorageConnection::closeWritable(WebCore::FileSystemHandleIdentifier identifier, WebCore::FileSystemWritableFileStreamIdentifier streamIdentifier, WebCore::FileSystemWriteCloseReason reason, VoidCallback&& completionHandler) { RefPtr connection = m_connection; if (!connection) return completionHandler(WebCore::Exception { WebCore::ExceptionCode::UnknownError, "Connection is lost"_s }); + m_writableIdentifiers.remove(streamIdentifier); connection->sendWithAsyncReply(Messages::NetworkStorageManager::CloseWritable(identifier, streamIdentifier, reason), [completionHandler = WTFMove(completionHandler)](auto error) mutable { completionHandler(convertToExceptionOr(error)); }); diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.h b/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.h index 8dd98799405b3..4da98397a737f 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.h +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.h @@ -97,11 +97,15 @@ class WebFileSystemStorageConnection final : public WebCore::FileSystemStorageCo void registerSyncAccessHandle(WebCore::FileSystemSyncAccessHandleIdentifier, WebCore::ScriptExecutionContextIdentifier) final; void unregisterSyncAccessHandle(WebCore::FileSystemSyncAccessHandleIdentifier) final; void invalidateAccessHandle(WebCore::FileSystemSyncAccessHandleIdentifier) final; - void createWritable(WebCore::FileSystemHandleIdentifier, bool keepExistingData, StreamCallback&&) final; + void createWritable(WebCore::ScriptExecutionContextIdentifier, WebCore::FileSystemHandleIdentifier, bool keepExistingData, StreamCallback&&) final; void closeWritable(WebCore::FileSystemHandleIdentifier, WebCore::FileSystemWritableFileStreamIdentifier, WebCore::FileSystemWriteCloseReason, VoidCallback&&) final; void executeCommandForWritable(WebCore::FileSystemHandleIdentifier, WebCore::FileSystemWritableFileStreamIdentifier, WebCore::FileSystemWriteCommandType, std::optional position, std::optional size, std::span dataBytes, bool hasDataError, VoidCallback&&) final; + void invalidateWritable(WebCore::FileSystemWritableFileStreamIdentifier); + void errorWritable(WebCore::ScriptExecutionContextIdentifier, WebCore::FileSystemWritableFileStreamIdentifier); + HashMap m_syncAccessHandles; + HashMap m_writableIdentifiers; RefPtr m_connection; }; diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.messages.in b/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.messages.in index 16f93fd7d4314..51b48bb8d62ca 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.messages.in +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebFileSystemStorageConnection.messages.in @@ -26,4 +26,5 @@ ] messages -> WebFileSystemStorageConnection { InvalidateAccessHandle(WebCore::FileSystemSyncAccessHandleIdentifier identifier) + InvalidateWritable(WebCore::FileSystemWritableFileStreamIdentifier identifier) } diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl index d44566faf05cc..af961310a80f5 100644 --- a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl +++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl @@ -76,6 +76,8 @@ interface TestRunner { undefined dumpResourceLoadStatistics(); undefined dumpPrivateClickMeasurement(); + undefined clearStorage(); + undefined clearDOMCaches(); undefined clearDOMCache(DOMString origin); boolean hasDOMCache(DOMString origin); diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp index 46ad295e8b208..ca6727406be80 100644 --- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp +++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp @@ -1820,6 +1820,11 @@ void TestRunner::clearDOMCache(JSStringRef origin) postSynchronousMessage("ClearDOMCache", toWK(origin)); } +void TestRunner::clearStorage() +{ + postSynchronousMessage("ClearStorage"); +} + void TestRunner::clearDOMCaches() { postSynchronousMessage("ClearDOMCaches"); diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h index 86ef6dcb9e170..954689ce5f1cd 100644 --- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h +++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h @@ -194,6 +194,8 @@ class TestRunner : public JSWrappable { JSRetainPtr pathToLocalResource(JSStringRef); void syncLocalStorage(); + void clearStorage(); + void clearDOMCache(JSStringRef origin); void clearDOMCaches(); bool hasDOMCache(JSStringRef origin); diff --git a/Tools/WebKitTestRunner/TestInvocation.cpp b/Tools/WebKitTestRunner/TestInvocation.cpp index 4da33a0d1ac46..190f077b7e91a 100644 --- a/Tools/WebKitTestRunner/TestInvocation.cpp +++ b/Tools/WebKitTestRunner/TestInvocation.cpp @@ -1218,6 +1218,11 @@ WKRetainPtr TestInvocation::didReceiveSynchronousMessageFromInjectedB return result; } + if (WKStringIsEqualToUTF8CString(messageName, "ClearStorage")) { + TestController::singleton().clearStorage(); + return nullptr; + } + if (WKStringIsEqualToUTF8CString(messageName, "ClearDOMCache")) { auto origin = stringValue(messageBody); TestController::singleton().clearDOMCache(origin); From b00c01c1d451d2b7c71cab53d0c0d0220e10e857 Mon Sep 17 00:00:00 2001 From: Richard Robinson Date: Sun, 23 Feb 2025 11:22:24 -0800 Subject: [PATCH 095/174] [SwiftUI] Custom `NavigationDeciding` implementations have no effect https://bugs.webkit.org/show_bug.cgi?id=288306 rdar://145390836 Reviewed by Abrar Rahman Protyasha. Consider the following simplified code: ``` protocol P { mutating func f() -> Int } extension P { func f() -> Int { 1 } } struct CustomP: P { func f() -> Int { 2 } } class C { let p: any P init(_ p: any P) { self.p = p } func proxyF() -> Int { p.f() } } let c = C(CustomP()) print(c.proxyF()) ``` This results in `1` being printed, since the `CustomP` implementation of `f` isn't used because it implements the `f` requirement of `P`, which is `mutating`, and therefore isn't allowed to be used with non-mutable value types. And so instead, the implementation of `f` inside the `extension P` is used instead since that one is considered a separate function entirely, and therefore does not implement the protocol requirement. Because 290468@main changed the NavigationDeciding protocol functions to become `mutating`, and since `WKNavigationDelegateAdapter` was using `let navigationDecider`, this subtle behavior difference was introduced, resulting in the default implementations always being used. Fix by using `var` instead of `let`, so that the mutating protocol requirement implementations in the custom deciders can actually be used. * Source/WebKit/UIProcess/API/Swift/WKNavigationDelegateAdapter.swift: (WKNavigationDelegateAdapter.navigationDecider): Canonical link: https://commits.webkit.org/290916@main --- .../UIProcess/API/Swift/WKNavigationDelegateAdapter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/WebKit/UIProcess/API/Swift/WKNavigationDelegateAdapter.swift b/Source/WebKit/UIProcess/API/Swift/WKNavigationDelegateAdapter.swift index 57c0ed43554f0..e6d38afbe5d57 100644 --- a/Source/WebKit/UIProcess/API/Swift/WKNavigationDelegateAdapter.swift +++ b/Source/WebKit/UIProcess/API/Swift/WKNavigationDelegateAdapter.swift @@ -42,7 +42,7 @@ final class WKNavigationDelegateAdapter: NSObject, WKNavigationDelegate { weak var owner: WebPage? = nil private let downloadProgressContinuation: AsyncStream.Continuation - private let navigationDecider: any WebPage.NavigationDeciding + private var navigationDecider: any WebPage.NavigationDeciding // MARK: Navigation progress reporting From 5764ec425e85f97b6a0c2197a6729140186cdb11 Mon Sep 17 00:00:00 2001 From: Yusuke Suzuki Date: Sun, 23 Feb 2025 12:46:11 -0800 Subject: [PATCH 096/174] [JSC] Add ThrowStackOverflowAtPrologue thunk https://bugs.webkit.org/show_bug.cgi?id=288304 rdar://145393291 Reviewed by Keith Miller. Add ThrowStackOverflowAtPrologue thunk, which is common in all code in JIT. * Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compile): (JSC::DFG::SpeculativeJIT::compileFunction): * Source/JavaScriptCore/ftl/FTLCompile.cpp: (JSC::FTL::compile): * Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::lower): * Source/JavaScriptCore/jit/JIT.cpp: (JSC::JIT::compileAndLinkWithoutFinalizing): * Source/JavaScriptCore/jit/JITThunks.h: * Source/JavaScriptCore/jit/ThunkGenerators.cpp: (JSC::throwStackOverflowAtPrologueGenerator): * Source/JavaScriptCore/jit/ThunkGenerators.h: Canonical link: https://commits.webkit.org/290917@main --- .../JavaScriptCore/dfg/DFGCodeOriginPool.cpp | 6 +++++ Source/JavaScriptCore/dfg/DFGCodeOriginPool.h | 2 ++ .../JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 19 ++-------------- Source/JavaScriptCore/ftl/FTLCompile.cpp | 5 +---- Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp | 22 +------------------ Source/JavaScriptCore/jit/JIT.cpp | 7 ++---- Source/JavaScriptCore/jit/JITThunks.h | 1 + Source/JavaScriptCore/jit/ThunkGenerators.cpp | 21 +++++++++++++++++- Source/JavaScriptCore/jit/ThunkGenerators.h | 1 + 9 files changed, 36 insertions(+), 48 deletions(-) diff --git a/Source/JavaScriptCore/dfg/DFGCodeOriginPool.cpp b/Source/JavaScriptCore/dfg/DFGCodeOriginPool.cpp index fff86e2c4b97a..7aa6d3eb95eb8 100644 --- a/Source/JavaScriptCore/dfg/DFGCodeOriginPool.cpp +++ b/Source/JavaScriptCore/dfg/DFGCodeOriginPool.cpp @@ -30,6 +30,12 @@ namespace JSC { namespace DFG { +CodeOriginPool::CodeOriginPool() +{ + // Ensure that CallSiteIndex 0 => non-inlined-function BytecodeIndex(0). + addCodeOrigin(CodeOrigin(BytecodeIndex(0))); +} + CallSiteIndex CodeOriginPool::addCodeOrigin(CodeOrigin codeOrigin) { if (m_codeOrigins.isEmpty() diff --git a/Source/JavaScriptCore/dfg/DFGCodeOriginPool.h b/Source/JavaScriptCore/dfg/DFGCodeOriginPool.h index 31c77a7f25c01..184d3f590cd67 100644 --- a/Source/JavaScriptCore/dfg/DFGCodeOriginPool.h +++ b/Source/JavaScriptCore/dfg/DFGCodeOriginPool.h @@ -51,6 +51,8 @@ class CodeOriginPool : public ThreadSafeRefCounted { unsigned size() const { return m_codeOrigins.size(); } private: + CodeOriginPool(); + Vector m_codeOrigins; Vector m_callSiteIndexFreeList; }; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 6524ac1962c73..2256f3f8f9fee 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -157,15 +157,7 @@ void SpeculativeJIT::compile() // // Generate the stack overflow handling; if the stack check in the entry head fails, // we need to call out to a helper function to throw the StackOverflowError. - stackOverflow.link(this); - - emitStoreCodeOrigin(CodeOrigin(BytecodeIndex(0))); - - if (maxFrameExtentForSlowPathCall) - addPtr(TrustedImm32(-static_cast(maxFrameExtentForSlowPathCall)), stackPointerRegister); - - emitGetFromCallFrameHeaderPtr(CallFrameSlot::codeBlock, GPRInfo::argumentGPR0); - callThrowOperationWithCallFrameRollback(operationThrowStackOverflowError, GPRInfo::argumentGPR0); + stackOverflow.linkThunk(CodeLocationLabel(vm().getCTIStub(CommonJITThunkID::ThrowStackOverflowAtPrologue).retaggedCode()), this); // Generate slow path code. runSlowPathGenerators(m_pcToCodeOriginMapBuilder); @@ -268,14 +260,7 @@ void SpeculativeJIT::compileFunction() stackOverflowWithEntry.link(this); compileEntry(); stackOverflow.link(this); - - emitStoreCodeOrigin(CodeOrigin(BytecodeIndex(0))); - - if (maxFrameExtentForSlowPathCall) - addPtr(TrustedImm32(-static_cast(maxFrameExtentForSlowPathCall)), stackPointerRegister); - - emitGetFromCallFrameHeaderPtr(CallFrameSlot::codeBlock, GPRInfo::argumentGPR0); - callThrowOperationWithCallFrameRollback(operationThrowStackOverflowError, GPRInfo::argumentGPR0); + jumpThunk(CodeLocationLabel(vm().getCTIStub(CommonJITThunkID::ThrowStackOverflowAtPrologue).retaggedCode())); // Generate slow path code. runSlowPathGenerators(m_pcToCodeOriginMapBuilder); diff --git a/Source/JavaScriptCore/ftl/FTLCompile.cpp b/Source/JavaScriptCore/ftl/FTLCompile.cpp index 9fc10628e218c..d81e8054caccc 100644 --- a/Source/JavaScriptCore/ftl/FTLCompile.cpp +++ b/Source/JavaScriptCore/ftl/FTLCompile.cpp @@ -172,10 +172,7 @@ void compile(State& state, Safepoint::Result& safepointResult) stackOverflowWithEntry.link(&jit); jit.emitFunctionPrologue(); - jit.move(CCallHelpers::TrustedImmPtr(codeBlock), GPRInfo::argumentGPR0); - jit.storePtr(GPRInfo::callFrameRegister, &vm.topCallFrame); - jit.callOperation(operationThrowStackOverflowError); - jit.jumpThunk(CodeLocationLabel(vm.getCTIStub(CommonJITThunkID::HandleExceptionWithCallFrameRollback).retaggedCode())); + jit.jumpThunk(CodeLocationLabel(vm.getCTIStub(CommonJITThunkID::ThrowStackOverflowAtPrologue).retaggedCode())); mainPathJumps.linkTo(mainPathLabel, &jit); } break; diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp index 9193e3347fb6b..711096c37615a 100644 --- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp +++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp @@ -287,7 +287,6 @@ class LowerDFGToB3 { // Stack Overflow Check. unsigned exitFrameSize = m_graph.requiredRegisterCountForExit() * sizeof(Register); PatchpointValue* stackOverflowHandler = m_out.patchpoint(Void); - CallSiteIndex callSiteIndex = callSiteIndexForCodeOrigin(m_ftlState, CodeOrigin(BytecodeIndex(0))); stackOverflowHandler->appendSomeRegister(m_callFrame); stackOverflowHandler->appendSomeRegister(m_vmValue); stackOverflowHandler->clobber(RegisterSetBuilder::macroClobberedGPRs()); @@ -318,26 +317,7 @@ class LowerDFGToB3 { // get clobbered. // https://bugs.webkit.org/show_bug.cgi?id=172456 jit.emitRestore(params.proc().calleeSaveRegisterAtOffsetList()); - - jit.store32( - MacroAssembler::TrustedImm32(callSiteIndex.bits()), - CCallHelpers::tagFor(CallFrameSlot::argumentCountIncludingThis)); - jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm->topEntryFrame, GPRInfo::argumentGPR0); - - jit.move(CCallHelpers::TrustedImmPtr(jit.codeBlock()), GPRInfo::argumentGPR0); - jit.prepareCallOperation(*vm); - CCallHelpers::Call throwCall = jit.call(OperationPtrTag); - - jit.move(CCallHelpers::TrustedImmPtr(vm), GPRInfo::argumentGPR0); - jit.prepareCallOperation(*vm); - CCallHelpers::Call lookupExceptionHandlerCall = jit.call(OperationPtrTag); - jit.jumpToExceptionHandler(*vm); - - jit.addLinkTask( - [=] (LinkBuffer& linkBuffer) { - linkBuffer.link(throwCall, operationThrowStackOverflowError); - linkBuffer.link(lookupExceptionHandlerCall, operationLookupExceptionHandlerFromCallerFrame); - }); + jit.jumpThunk(CodeLocationLabel(vm->getCTIStub(CommonJITThunkID::ThrowStackOverflowAtPrologue).retaggedCode())); }); }); diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp index cf7181b649a8e..8de49fb999ea0 100644 --- a/Source/JavaScriptCore/jit/JIT.cpp +++ b/Source/JavaScriptCore/jit/JIT.cpp @@ -858,12 +858,9 @@ RefPtr JIT::compileAndLinkWithoutFinalizing(JITCompilationEffor stackOverflowWithEntry.link(this); emitFunctionPrologue(); - stackOverflow.link(this); m_bytecodeIndex = BytecodeIndex(0); - if (maxFrameExtentForSlowPathCall) - addPtr(TrustedImm32(-static_cast(maxFrameExtentForSlowPathCall)), stackPointerRegister); - emitGetFromCallFrameHeaderPtr(CallFrameSlot::codeBlock, regT0); - callThrowOperationWithCallFrameRollback(operationThrowStackOverflowError, regT0); + stackOverflow.link(this); + jumpThunk(CodeLocationLabel(vm().getCTIStub(CommonJITThunkID::ThrowStackOverflowAtPrologue).retaggedCode())); ASSERT(m_jmpTable.isEmpty()); diff --git a/Source/JavaScriptCore/jit/JITThunks.h b/Source/JavaScriptCore/jit/JITThunks.h index d66103610a488..bb3afd6dd3fbd 100644 --- a/Source/JavaScriptCore/jit/JITThunks.h +++ b/Source/JavaScriptCore/jit/JITThunks.h @@ -64,6 +64,7 @@ class NativeExecutable; macro(InternalFunctionConstruct, internalFunctionConstructGenerator) \ macro(ThrowExceptionFromCall, throwExceptionFromCallGenerator) \ macro(ThrowExceptionFromCallSlowPath, throwExceptionFromCallSlowPathGenerator) \ + macro(ThrowStackOverflowAtPrologue, throwStackOverflowAtPrologueGenerator) \ macro(VirtualThunkForRegularCall, virtualThunkForRegularCall) \ macro(VirtualThunkForTailCall, virtualThunkForTailCall) \ macro(VirtualThunkForConstruct, virtualThunkForConstruct) \ diff --git a/Source/JavaScriptCore/jit/ThunkGenerators.cpp b/Source/JavaScriptCore/jit/ThunkGenerators.cpp index 4db8c0500566f..164ea6ce7904f 100644 --- a/Source/JavaScriptCore/jit/ThunkGenerators.cpp +++ b/Source/JavaScriptCore/jit/ThunkGenerators.cpp @@ -185,6 +185,25 @@ MacroAssemblerCodeRef throwExceptionFromCallSlowPathGenerator(VM return FINALIZE_THUNK(patchBuffer, JITThunkPtrTag, "throwExceptionFromCallSlowPath"_s, "Throw exception from call slow path thunk"); } +MacroAssemblerCodeRef throwStackOverflowAtPrologueGenerator(VM& vm) +{ + CCallHelpers jit; + + if (maxFrameExtentForSlowPathCall) + jit.addPtr(CCallHelpers::TrustedImm32(-static_cast(maxFrameExtentForSlowPathCall)), CCallHelpers::stackPointerRegister); + + // In all tiers (LLInt, Baseline, DFG, and FTL), CodeOrigin(BytecodeIndex(0)) is zero, or CallSiteIndex(0) is pointint at CodeOrigin(BytecodeIndex(0)). + jit.store32(CCallHelpers::TrustedImm32(0), CCallHelpers::tagFor(CallFrameSlot::argumentCountIncludingThis)); + + jit.emitGetFromCallFrameHeaderPtr(CallFrameSlot::codeBlock, GPRInfo::argumentGPR0); + jit.prepareCallOperation(vm); + jit.callOperation(operationThrowStackOverflowError); + jit.jumpThunk(CodeLocationLabel(vm.getCTIStub(CommonJITThunkID::HandleExceptionWithCallFrameRollback).retaggedCode())); + + LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, LinkBuffer::Profile::Thunk); + return FINALIZE_THUNK(patchBuffer, JITThunkPtrTag, "throwStackOverflow"_s, "throwStackOverflow"); +} + // FIXME: We should distinguish between a megamorphic virtual call vs. a slow // path virtual call so that we can enable fast tail calls for megamorphic // virtual calls by using the shuffler. @@ -283,7 +302,7 @@ static MacroAssemblerCodeRef virtualThunkFor(VM& vm, CallMode mo jit.farJump(GPRInfo::returnValueGPR, JSEntryPtrTag); LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, LinkBuffer::Profile::InlineCache); - return FINALIZE_THUNK(patchBuffer, JITThunkPtrTag, "VirtualCall"_s "Virtual %s thunk", mode == CallMode::Regular ? "call" : mode == CallMode::Tail ? "tail call" : "construct"); + return FINALIZE_THUNK(patchBuffer, JITThunkPtrTag, "VirtualCall"_s, "Virtual %s thunk", mode == CallMode::Regular ? "call" : mode == CallMode::Tail ? "tail call" : "construct"); } MacroAssemblerCodeRef virtualThunkForRegularCall(VM& vm) diff --git a/Source/JavaScriptCore/jit/ThunkGenerators.h b/Source/JavaScriptCore/jit/ThunkGenerators.h index 687ac32dd8960..3ee58534d1a9e 100644 --- a/Source/JavaScriptCore/jit/ThunkGenerators.h +++ b/Source/JavaScriptCore/jit/ThunkGenerators.h @@ -44,6 +44,7 @@ MacroAssemblerCodeRef popThunkStackPreservesAndHandleExceptionGe MacroAssemblerCodeRef throwExceptionFromCallGenerator(VM&); MacroAssemblerCodeRef throwExceptionFromCallSlowPathGenerator(VM&); +MacroAssemblerCodeRef throwStackOverflowAtPrologueGenerator(VM&); MacroAssemblerCodeRef checkExceptionGenerator(VM&); MacroAssemblerCodeRef returnFromBaselineGenerator(VM&); From a8406fbeeaec52a22238a482f7948bb150de97ce Mon Sep 17 00:00:00 2001 From: Yusuke Suzuki Date: Sun, 23 Feb 2025 12:48:10 -0800 Subject: [PATCH 097/174] [JSC] Simplify AssemblyHelpers::purifyNaN https://bugs.webkit.org/show_bug.cgi?id=288302 rdar://145390959 Reviewed by Keith Miller. Simplify the implementation of AssemblyHelpers::purifyNaN by using fpTempRegister. * Source/JavaScriptCore/bytecode/InlineCacheCompiler.cpp: (JSC::InlineCacheCompiler::generateWithGuard): * Source/JavaScriptCore/dfg/DFGOSRExit.cpp: (JSC::DFG::OSRExit::compileExit): * Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileValueRep): * Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp: (JSC::FTL::reboxAccordingToFormat): * Source/JavaScriptCore/jit/AssemblyHelpers.cpp: (JSC::AssemblyHelpers::purifyNaN): * Source/JavaScriptCore/jit/AssemblyHelpers.h: * Source/JavaScriptCore/jit/CallFrameShuffler64.cpp: (JSC::CallFrameShuffler::emitBox): * Source/JavaScriptCore/wasm/js/WasmToJS.cpp: (JSC::Wasm::wasmToJS): Canonical link: https://commits.webkit.org/290918@main --- .../bytecode/InlineCacheCompiler.cpp | 2 +- Source/JavaScriptCore/dfg/DFGOSRExit.cpp | 4 ++-- .../JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 20 +++++++++++-------- .../JavaScriptCore/ftl/FTLOSRExitCompiler.cpp | 2 +- Source/JavaScriptCore/jit/AssemblyHelpers.cpp | 13 +++++++++--- Source/JavaScriptCore/jit/AssemblyHelpers.h | 2 +- .../jit/CallFrameShuffler64.cpp | 2 +- Source/JavaScriptCore/wasm/js/WasmToJS.cpp | 2 +- 8 files changed, 29 insertions(+), 18 deletions(-) diff --git a/Source/JavaScriptCore/bytecode/InlineCacheCompiler.cpp b/Source/JavaScriptCore/bytecode/InlineCacheCompiler.cpp index 31ee5b3feff5f..517798ec2aac0 100644 --- a/Source/JavaScriptCore/bytecode/InlineCacheCompiler.cpp +++ b/Source/JavaScriptCore/bytecode/InlineCacheCompiler.cpp @@ -2238,7 +2238,7 @@ void InlineCacheCompiler::generateWithGuard(unsigned index, AccessCase& accessCa CRASH(); } - jit.purifyNaN(m_scratchFPR); + jit.purifyNaN(m_scratchFPR, m_scratchFPR); jit.boxDouble(m_scratchFPR, valueRegs); } } diff --git a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp index 64c3851f971d8..f5cc9a3138dcb 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp @@ -644,7 +644,7 @@ void OSRExit::compileExit(CCallHelpers& jit, VM& vm, const OSRExit& exit, const case UnboxedDoubleInFPR: jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT1); jit.loadDouble(MacroAssembler::Address(GPRInfo::regT1), FPRInfo::fpRegT0); - jit.purifyNaN(FPRInfo::fpRegT0); + jit.purifyNaN(FPRInfo::fpRegT0, FPRInfo::fpRegT0); #if USE(JSVALUE64) jit.boxDouble(FPRInfo::fpRegT0, GPRInfo::regT0); jit.store64(GPRInfo::regT0, MacroAssembler::Address(GPRInfo::regT1)); @@ -656,7 +656,7 @@ void OSRExit::compileExit(CCallHelpers& jit, VM& vm, const OSRExit& exit, const case DoubleDisplacedInJSStack: jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT1); jit.loadDouble(AssemblyHelpers::addressFor(recovery.virtualRegister()), FPRInfo::fpRegT0); - jit.purifyNaN(FPRInfo::fpRegT0); + jit.purifyNaN(FPRInfo::fpRegT0, FPRInfo::fpRegT0); #if USE(JSVALUE64) jit.boxDouble(FPRInfo::fpRegT0, GPRInfo::regT0); jit.store64(GPRInfo::regT0, MacroAssembler::Address(GPRInfo::regT1)); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 2256f3f8f9fee..a4574343e825a 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -3090,12 +3090,17 @@ void SpeculativeJIT::compileValueRep(Node* node) // anymore. Unfortunately, this would be unsound. If it's a GetLocal or if the value was // subject to a prior SetLocal, filtering the value would imply that the corresponding // local was purified. - if (m_state.forNode(node->child1()).couldBeType(SpecDoubleImpureNaN)) - purifyNaN(valueFPR); + if (m_state.forNode(node->child1()).couldBeType(SpecDoubleImpureNaN)) { + FPRTemporary temp(this); + FPRReg tempFPR = temp.fpr(); - boxDouble(valueFPR, resultRegs); - - jsValueResult(resultRegs, node); + purifyNaN(valueFPR, tempFPR); + boxDouble(tempFPR, resultRegs); + jsValueResult(resultRegs, node); + } else { + boxDouble(valueFPR, resultRegs); + jsValueResult(resultRegs, node); + } return; } @@ -3697,7 +3702,7 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(Node* node, TypedArrayType } if (format == DataFormatJS) { - purifyNaN(resultReg); + purifyNaN(resultReg, resultReg); boxDouble(resultReg, resultRegs); if (jump.isSet()) jump.link(this); @@ -6754,8 +6759,7 @@ void SpeculativeJIT::compilePurifyNaN(Node* node) FPRReg valueFPR = value.fpr(); FPRReg resultFPR = result.fpr(); - moveDouble(valueFPR, resultFPR); - purifyNaN(resultFPR); + purifyNaN(valueFPR, resultFPR); doubleResult(resultFPR, node); } diff --git a/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp b/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp index 0491f30fab306..750dbb69ff68d 100644 --- a/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp +++ b/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp @@ -91,7 +91,7 @@ static void reboxAccordingToFormat( case DataFormatDouble: { jit.moveDoubleTo64(FPRInfo::fpRegT0, scratch1); jit.move64ToDouble(value, FPRInfo::fpRegT0); - jit.purifyNaN(FPRInfo::fpRegT0); + jit.purifyNaN(FPRInfo::fpRegT0, FPRInfo::fpRegT0); jit.boxDouble(FPRInfo::fpRegT0, value); jit.move64ToDouble(scratch1, FPRInfo::fpRegT0); break; diff --git a/Source/JavaScriptCore/jit/AssemblyHelpers.cpp b/Source/JavaScriptCore/jit/AssemblyHelpers.cpp index 737db6f6e5099..4b6fb5dada3a2 100644 --- a/Source/JavaScriptCore/jit/AssemblyHelpers.cpp +++ b/Source/JavaScriptCore/jit/AssemblyHelpers.cpp @@ -82,11 +82,18 @@ void AssemblyHelpers::decrementSuperSamplerCount() sub32(TrustedImm32(1), AbsoluteAddress(std::bit_cast(&g_superSamplerCount))); } -void AssemblyHelpers::purifyNaN(FPRReg fpr) +void AssemblyHelpers::purifyNaN(FPRReg inputFPR, FPRReg resultFPR) { - MacroAssembler::Jump notNaN = branchIfNotNaN(fpr); - move64ToDouble(TrustedImm64(std::bit_cast(PNaN)), fpr); + ASSERT(inputFPR != fpTempRegister); +#if CPU(ADDRESS64) + move64ToDouble(TrustedImm64(std::bit_cast(PNaN)), fpTempRegister); + moveDoubleConditionallyDouble(DoubleEqualAndOrdered, inputFPR, inputFPR, inputFPR, fpTempRegister, resultFPR); +#else + moveDouble(inputFPR, resultFPR); + auto notNaN = branchIfNotNaN(resultFPR); + move64ToDouble(TrustedImm64(std::bit_cast(PNaN)), resultFPR); notNaN.link(this); +#endif } #if ENABLE(SAMPLING_FLAGS) diff --git a/Source/JavaScriptCore/jit/AssemblyHelpers.h b/Source/JavaScriptCore/jit/AssemblyHelpers.h index 6dc9303108256..7088e382c88a9 100644 --- a/Source/JavaScriptCore/jit/AssemblyHelpers.h +++ b/Source/JavaScriptCore/jit/AssemblyHelpers.h @@ -1450,7 +1450,7 @@ class AssemblyHelpers : public MacroAssembler { void incrementSuperSamplerCount(); void decrementSuperSamplerCount(); - void purifyNaN(FPRReg); + void purifyNaN(FPRReg, FPRReg); // These methods convert between doubles, and doubles boxed and JSValues. #if USE(JSVALUE64) diff --git a/Source/JavaScriptCore/jit/CallFrameShuffler64.cpp b/Source/JavaScriptCore/jit/CallFrameShuffler64.cpp index 2e3fc3025ab74..d627be5d840e4 100644 --- a/Source/JavaScriptCore/jit/CallFrameShuffler64.cpp +++ b/Source/JavaScriptCore/jit/CallFrameShuffler64.cpp @@ -146,7 +146,7 @@ void CallFrameShuffler::emitBox(CachedRecovery& cachedRecovery) resultGPR = getFreeGPR(); ASSERT(resultGPR != InvalidGPRReg); ASSERT(Reg::fromIndex(resultGPR).isGPR()); - m_jit.purifyNaN(cachedRecovery.recovery().fpr()); + m_jit.purifyNaN(cachedRecovery.recovery().fpr(), cachedRecovery.recovery().fpr()); m_jit.moveDoubleTo64(cachedRecovery.recovery().fpr(), resultGPR); m_lockedRegisters.add(resultGPR, IgnoreVectors); if (tryAcquireNumberTagRegister()) diff --git a/Source/JavaScriptCore/wasm/js/WasmToJS.cpp b/Source/JavaScriptCore/wasm/js/WasmToJS.cpp index 50a39fb009a9c..d1f3253126a14 100644 --- a/Source/JavaScriptCore/wasm/js/WasmToJS.cpp +++ b/Source/JavaScriptCore/wasm/js/WasmToJS.cpp @@ -224,7 +224,7 @@ Expected, BindingFailure> wasmToJS(TypeIn unsigned frOffset = CallFrameSlot::firstArgument * static_cast(sizeof(Register)); auto marshallFPR = [&] (FPRReg fprReg) { - jit.purifyNaN(fprReg); + jit.purifyNaN(fprReg, fprReg); #if USE(JSVALUE64) jit.moveDoubleTo64(fprReg, scratch); materializeDoubleEncodeOffset(doubleEncodeOffsetGPRReg); From 4d590ffaba704034d9c107d3c485ba8e9ef81fa5 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Sun, 23 Feb 2025 13:57:47 -0800 Subject: [PATCH 098/174] Support ContentVisibilityForceLayout in updateLayoutIfDimensionsOutOfDate. https://bugs.webkit.org/show_bug.cgi?id=288312 Reviewed by Alan Baradlay. * Source/WebCore/dom/Document.cpp: (WebCore::Document::updateLayoutIfDimensionsOutOfDate): * Source/WebCore/dom/Document.h: (WebCore::Document::updateLayoutIfDimensionsOutOfDate): * Source/WebCore/rendering/RenderBlock.cpp: (WebCore::RenderBlock::canPerformSimplifiedLayout const): (WebCore::RenderBlock::simplifiedLayout): * Source/WebCore/rendering/RenderBlock.h: Canonical link: https://commits.webkit.org/290919@main --- Source/WebCore/dom/Document.cpp | 17 +++++++++++++---- Source/WebCore/dom/Document.h | 2 +- Source/WebCore/rendering/RenderBlock.cpp | 7 +++---- Source/WebCore/rendering/RenderBlock.h | 3 ++- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp index b3859b03630d3..d53a3456139a7 100644 --- a/Source/WebCore/dom/Document.cpp +++ b/Source/WebCore/dom/Document.cpp @@ -2998,13 +2998,13 @@ std::unique_ptr Document::styleForElementIgnoringPendingStylesheets return WTFMove(elementStyle.style); } -bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, OptionSet dimensionsCheck) +bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, OptionSet dimensionsCheck, OptionSet layoutOptions) { ASSERT(isMainThread()); // If the stylesheets haven't loaded, just give up and do a full layout ignoring pending stylesheets. if (!haveStylesheetsLoaded()) { - updateLayoutIgnorePendingStylesheets(); + updateLayoutIgnorePendingStylesheets(layoutOptions); return true; } @@ -3021,7 +3021,7 @@ bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, OptionSetprotectedDocument()->updateLayoutIfDimensionsOutOfDate(*owner)) { - updateLayout({ }, &element); + updateLayout(layoutOptions, &element); return true; } } @@ -3030,6 +3030,15 @@ bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, OptionSetstyle().hasSkippedContent()) { + if (auto wasSkippedDuringLastLayout = renderer->wasSkippedDuringLastLayoutDueToContentVisibility()) { + if (*wasSkippedDuringLastLayout) + renderer->setNeedsLayout(); + } + } + } + if (!element.renderer() || !frameView->layoutContext().needsLayout()) { // Tree is clean, we don't need to walk the ancestor chain to figure out whether we have a sufficiently clean subtree. return false; @@ -3102,7 +3111,7 @@ bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, OptionSet styleUpdate); - bool updateLayoutIfDimensionsOutOfDate(Element&, OptionSet = { DimensionsCheck::Width, DimensionsCheck::Height }); + bool updateLayoutIfDimensionsOutOfDate(Element&, OptionSet = { DimensionsCheck::Width, DimensionsCheck::Height }, OptionSet = { }); inline AXObjectCache* existingAXObjectCache() const; WEBCORE_EXPORT AXObjectCache* axObjectCache() const; diff --git a/Source/WebCore/rendering/RenderBlock.cpp b/Source/WebCore/rendering/RenderBlock.cpp index 58f1b3f9da81d..cecf62dea2009 100644 --- a/Source/WebCore/rendering/RenderBlock.cpp +++ b/Source/WebCore/rendering/RenderBlock.cpp @@ -839,6 +839,8 @@ bool RenderBlock::canPerformSimplifiedLayout() const return false; if (auto wasSkippedDuringLastLayout = wasSkippedDuringLastLayoutDueToContentVisibility(); wasSkippedDuringLastLayout && *wasSkippedDuringLastLayout) return false; + if (layoutContext().isSkippedContentRootForLayout(*this) && (posChildNeedsLayout() || canContainFixedPositionObjects())) + return false; return posChildNeedsLayout() || needsSimplifiedNormalFlowLayout(); } @@ -851,10 +853,6 @@ bool RenderBlock::simplifiedLayout() if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly()) return false; - bool canContainFixedPosObjects = canContainFixedPositionObjects(); - if (layoutContext().isSkippedContentRootForLayout(*this) && (posChildNeedsLayout() || canContainFixedPosObjects)) - return false; - // Lay out positioned descendants or objects that just need to recompute overflow. if (needsSimplifiedNormalFlowLayout()) simplifiedNormalFlowLayout(); @@ -869,6 +867,7 @@ bool RenderBlock::simplifiedLayout() // child, neither the fixed element nor its container learn of the movement since posChildNeedsLayout() is only marked as far as the // relative positioned container. So if we can have fixed pos objects in our positioned objects list check if any of them // are statically positioned and thus need to move with their absolute ancestors. + bool canContainFixedPosObjects = canContainFixedPositionObjects(); if (posChildNeedsLayout() || canContainFixedPosObjects) layoutPositionedObjects(RelayoutChildren::No, !posChildNeedsLayout() && canContainFixedPosObjects); diff --git a/Source/WebCore/rendering/RenderBlock.h b/Source/WebCore/rendering/RenderBlock.h index 465acee5760b1..59f510c3bbad3 100644 --- a/Source/WebCore/rendering/RenderBlock.h +++ b/Source/WebCore/rendering/RenderBlock.h @@ -257,6 +257,8 @@ class RenderBlock : public RenderBox { void updateDescendantTransformsAfterLayout(); + virtual bool canPerformSimplifiedLayout() const; + protected: RenderFragmentedFlow* locateEnclosingFragmentedFlow() const override; bool establishesIndependentFormattingContextIgnoringDisplayType(const RenderStyle&) const; @@ -298,7 +300,6 @@ class RenderBlock : public RenderBox { void styleWillChange(StyleDifference, const RenderStyle& newStyle) override; void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override; - virtual bool canPerformSimplifiedLayout() const; bool simplifiedLayout(); virtual void simplifiedNormalFlowLayout(); From 213a4d7e07fd0c8446d642220d920e387f735907 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Sun, 23 Feb 2025 14:11:36 -0800 Subject: [PATCH 099/174] Address safer cpp failures in MediaRecorderPrivateWriterWebM.cpp https://bugs.webkit.org/show_bug.cgi?id=288307 Reviewed by Geoffrey Garen. * Source/WebCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations: * Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterWebM.cpp: (WebCore::MediaRecorderPrivateWriterWebMDelegate::addAudioTrack): (WebCore::MediaRecorderPrivateWriterWebMDelegate::addVideoTrack): Canonical link: https://commits.webkit.org/290920@main --- .../MemoryUnsafeCastCheckerExpectations | 1 - .../cocoa/MediaRecorderPrivateWriterWebM.cpp | 12 ++++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/WebCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations b/Source/WebCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations index 0db34133fab3e..64613bae81355 100644 --- a/Source/WebCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/MemoryUnsafeCastCheckerExpectations @@ -120,7 +120,6 @@ platform/mac/ScrollbarThemeMac.mm platform/mac/ScrollbarsControllerMac.mm platform/mac/SerializedPlatformDataCueMac.mm platform/mac/WebCoreFullScreenWindow.mm -platform/mediarecorder/cocoa/MediaRecorderPrivateWriterWebM.cpp platform/network/BlobResourceHandle.cpp rendering/BidiRun.cpp rendering/BidiRun.h diff --git a/Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterWebM.cpp b/Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterWebM.cpp index c6f806bfad3c3..3e4f710453fa0 100644 --- a/Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterWebM.cpp +++ b/Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterWebM.cpp @@ -40,6 +40,14 @@ #import +SPECIALIZE_TYPE_TRAITS_BEGIN(mkvmuxer::AudioTrack) + static bool isType(const mkvmuxer::Track& track) { return track.type() == mkvmuxer::Tracks::kAudio; } +SPECIALIZE_TYPE_TRAITS_END() + +SPECIALIZE_TYPE_TRAITS_BEGIN(mkvmuxer::VideoTrack) + static bool isType(const mkvmuxer::Track& track) { return track.type() == mkvmuxer::Tracks::kVideo; } +SPECIALIZE_TYPE_TRAITS_END() + namespace WebCore { WTF_MAKE_TZONE_ALLOCATED_IMPL(MediaRecorderPrivateWriterWebM); @@ -97,7 +105,7 @@ class MediaRecorderPrivateWriterWebMDelegate : public mkvmuxer::IMkvWriter { auto trackIndex = m_segment.AddAudioTrack(info.rate, info.channels, 0); if (!trackIndex) return { }; - auto* audioTrack = reinterpret_cast(m_segment.GetTrackByNumber(trackIndex)); + auto* audioTrack = downcast(m_segment.GetTrackByNumber(trackIndex)); ASSERT(audioTrack); audioTrack->set_bit_depth(32u); audioTrack->set_codec_id(mkvCodeIcForMediaVideoCodecId(info.codecName)); @@ -115,7 +123,7 @@ class MediaRecorderPrivateWriterWebMDelegate : public mkvmuxer::IMkvWriter { auto trackIndex = m_segment.AddVideoTrack(info.size.width(), info.size.height(), 0); if (!trackIndex) return { }; - auto* videoTrack = reinterpret_cast(m_segment.GetTrackByNumber(trackIndex)); + auto* videoTrack = downcast(m_segment.GetTrackByNumber(trackIndex)); ASSERT(videoTrack); videoTrack->set_codec_id(mkvCodeIcForMediaVideoCodecId(info.codecName)); if (RefPtr atomData = info.atomData; atomData && atomData->span().size()) From cd23c73ac692918316f2e345fc313f8fd0bbc79b Mon Sep 17 00:00:00 2001 From: Charlie Wolfe Date: Sun, 23 Feb 2025 14:22:11 -0800 Subject: [PATCH 100/174] Remove unnecessary sync IPC in NavigationScheduler https://bugs.webkit.org/show_bug.cgi?id=288298 rdar://145382743 Reviewed by Sihui Liu. BackForwardController::currentItem sends a sync IPC message. We should avoid using it twice in these functions. * Source/WebCore/loader/NavigationScheduler.cpp: Canonical link: https://commits.webkit.org/290921@main --- Source/WebCore/loader/NavigationScheduler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/WebCore/loader/NavigationScheduler.cpp b/Source/WebCore/loader/NavigationScheduler.cpp index 8bfa8639622ba..5f70f737d06ce 100644 --- a/Source/WebCore/loader/NavigationScheduler.cpp +++ b/Source/WebCore/loader/NavigationScheduler.cpp @@ -303,7 +303,7 @@ class ScheduledHistoryNavigation : public ScheduledNavigation { UserGestureIndicator gestureIndicator(userGestureToForward()); - if (page->checkedBackForward()->currentItem() && page->checkedBackForward()->currentItem()->itemID() == m_historyItem->itemID()) { + if (RefPtr currentItem = page->checkedBackForward()->currentItem(); currentItem && currentItem->itemID() == m_historyItem->itemID()) { localFrame->protectedLoader()->changeLocation(localFrame->document()->url(), selfTargetFrameName(), 0, ReferrerPolicy::EmptyString, shouldOpenExternalURLs(), std::nullopt, nullAtom(), std::nullopt, NavigationHistoryBehavior::Reload); return; } @@ -380,7 +380,7 @@ class ScheduledHistoryNavigationByKey : public ScheduledNavigation { UserGestureIndicator gestureIndicator(userGestureToForward()); - if (page->backForward().currentItem() && page->backForward().currentItem()->itemID() == (*historyItem)->itemID()) { + if (RefPtr currentItem = page->checkedBackForward()->currentItem(); currentItem && currentItem->itemID() == (*historyItem)->itemID()) { if (RefPtr localFrame = dynamicDowncast(frame)) localFrame->protectedLoader()->changeLocation(localFrame->document()->url(), selfTargetFrameName(), 0, ReferrerPolicy::EmptyString, shouldOpenExternalURLs(), std::nullopt, nullAtom(), std::nullopt, NavigationHistoryBehavior::Reload); return; From 2172f7b5f9f2ae8cd4ab6795400b6d3eb6c61f94 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Sun, 23 Feb 2025 14:50:52 -0800 Subject: [PATCH 101/174] getBoundingClientRect spends time flushing layout even if the current layout state is sufficiently valid. https://bugs.webkit.org/show_bug.cgi?id=287880 Reviewed by Alan Baradlay. Extends updateLayoutIfDimensionsOutOfDate to pass through LayoutOptions to the final layout if required. Adds Top and Left options, and rejects the fast path if any ancestor requires position movement layout (on top of any ancestor requiring normal self layout). Allows updateLayoutIfDimensionsOutOfDate to succeed if the queried renderer requires only simplified layout, and it will succeed. Start using updateLayoutIfDimensionsOutOfDate for getBoundingClientRect. Moves a check from RenderBlock::simplifiedLayout() to RenderBlock::canPerformSimplifiedLayout() (and make the latter public) so that it correctly determines if the simplified layout will succeed. * Source/WebCore/dom/Document.cpp: (WebCore::Document::updateLayoutIfDimensionsOutOfDate): * Source/WebCore/dom/Document.h: (WebCore::Document::updateLayoutIfDimensionsOutOfDate): * Source/WebCore/dom/Element.cpp: (WebCore::Element::boundingClientRect): * Source/WebCore/rendering/RenderBlock.cpp: (WebCore::RenderBlock::canPerformSimplifiedLayout const): (WebCore::RenderBlock::simplifiedLayout): * Source/WebCore/rendering/RenderBlock.h: Canonical link: https://commits.webkit.org/290922@main --- ...ain-paint-offscreen-container-expected.txt | 2 +- Source/WebCore/dom/Document.cpp | 22 +++++++++++++++++-- Source/WebCore/dom/Document.h | 7 ++++-- Source/WebCore/dom/Element.cpp | 2 +- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-anchoring/contain-paint-offscreen-container-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-anchoring/contain-paint-offscreen-container-expected.txt index d66b0c17a55d1..5280049ebbd21 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-anchoring/contain-paint-offscreen-container-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-anchoring/contain-paint-offscreen-container-expected.txt @@ -1,3 +1,3 @@ -FAIL Contain: style paint container offscreen. assert_equals: expected -2 but got -102 +PASS Contain: style paint container offscreen. diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp index d53a3456139a7..02c48710cfee5 100644 --- a/Source/WebCore/dom/Document.cpp +++ b/Source/WebCore/dom/Document.cpp @@ -3044,14 +3044,27 @@ bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, OptionSetneedsLayout() || is(element); + bool requireFullLayout = is(element); + { + CheckedPtr renderer = element.renderer(); + if (renderer->selfNeedsLayout() || renderer->normalChildNeedsLayout() || renderer->posChildNeedsLayout() || renderer->needsPositionedMovementLayout()) + requireFullLayout = true; + if (renderer->needsSimplifiedNormalFlowLayout()) { + if (!dimensionsCheck.contains(DimensionsCheck::IgnoreOverflow)) + requireFullLayout = true; + else if (CheckedPtr block = dynamicDowncast(*renderer); !dimensionsCheck.contains(DimensionsCheck::IgnoreOverflow) || !block || !block->canPerformSimplifiedLayout()) + requireFullLayout = true; + } + } + if (!requireFullLayout) { CheckedPtr previousBox; CheckedPtr currentBox; CheckedPtr renderer = element.renderer(); + bool hasSpecifiedLogicalHeight = renderer->style().logicalMinHeight() == Length(0, LengthType::Fixed) && renderer->style().logicalHeight().isFixed() && renderer->style().logicalMaxHeight().isAuto(); bool isVertical = !renderer->isHorizontalWritingMode(); bool checkingLogicalWidth = (dimensionsCheck.contains(DimensionsCheck::Width) && !isVertical) || (dimensionsCheck.contains(DimensionsCheck::Height) && isVertical); @@ -3074,6 +3087,11 @@ bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, OptionSetneedsPositionedMovementLayout() || currentBox->normalChildNeedsLayout())) { + requireFullLayout = true; + break; + } + // If a box needs layout for itself or if a box has changed children and sizes its width to // its content, then require a full layout. if (currentBox->selfNeedsLayout() || diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h index 296d2b973e8af..b4ccbb9769ec9 100644 --- a/Source/WebCore/dom/Document.h +++ b/Source/WebCore/dom/Document.h @@ -366,8 +366,11 @@ enum class EventHandlerRemoval : bool { One, All }; using EventTargetSet = WeakHashCountedSet; enum class DimensionsCheck : uint8_t { - Width = 1 << 0, - Height = 1 << 1 + Left = 1 << 0, + Top = 1 << 1, + Width = 1 << 2, + Height = 1 << 3, + IgnoreOverflow = 1 << 4, }; enum class HttpEquivPolicy : uint8_t { diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp index 9b35c7c1a8316..b6a103b05ac06 100644 --- a/Source/WebCore/dom/Element.cpp +++ b/Source/WebCore/dom/Element.cpp @@ -1935,7 +1935,7 @@ std::optional, FloatRect>> Element::boundingA FloatRect Element::boundingClientRect() { Ref document = this->document(); - document->updateLayoutIgnorePendingStylesheets({ LayoutOptions::ContentVisibilityForceLayout, LayoutOptions::CanDeferUpdateLayerPositions }, this); + document->updateLayoutIfDimensionsOutOfDate(*this, { DimensionsCheck::Left, DimensionsCheck::Top, DimensionsCheck::Width, DimensionsCheck::Height, DimensionsCheck::IgnoreOverflow }, { LayoutOptions::ContentVisibilityForceLayout, LayoutOptions::CanDeferUpdateLayerPositions, LayoutOptions::IgnorePendingStylesheets }); LocalFrameView::AutoPreventLayerAccess preventAccess(document->view()); auto pair = boundingAbsoluteRectWithoutLayout(); if (!pair) From 4d082633f9d78ef54c78b7d7d68b4574a51e6a64 Mon Sep 17 00:00:00 2001 From: Sam Weinig Date: Sun, 23 Feb 2025 17:54:22 -0800 Subject: [PATCH 102/174] Fix warnings seen when running process-css-properties.py https://bugs.webkit.org/show_bug.cgi?id=288310 Reviewed by Darin Adler. - Adds missing non-standard "intrinsic" value to the values array for sizing properties. - Adds missing non-standard "auto" and "match-element" values to the values array for view-transition-name. - Disable warnings for properties that use a non-builtin reference term in their grammar. These grammars are incomplete from the generators perspective, so they must be treated like any other custom parsing. * Source/WebCore/css/CSSProperties.json: * Source/WebCore/css/process-css-properties.py: Canonical link: https://commits.webkit.org/290923@main --- Source/WebCore/css/CSSProperties.json | 62 +++++++++++++++++++- Source/WebCore/css/process-css-properties.py | 49 +++++++++++++++- 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/Source/WebCore/css/CSSProperties.json b/Source/WebCore/css/CSSProperties.json index bcaf1a2af39b6..f18d8b1634dbb 100644 --- a/Source/WebCore/css/CSSProperties.json +++ b/Source/WebCore/css/CSSProperties.json @@ -2223,6 +2223,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -4241,6 +4245,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -4352,6 +4360,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -5531,6 +5543,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -5584,6 +5600,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -5635,6 +5655,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -5706,6 +5730,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -5757,6 +5785,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -5810,6 +5842,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -5861,6 +5897,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -5914,6 +5954,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -7761,6 +7805,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -8946,6 +8994,10 @@ "value": "contain", "status": "unimplemented" }, + { + "value": "intrinsic", + "status": "non-standard" + }, { "value": "min-intrinsic", "status": "non-standard" @@ -11328,7 +11380,15 @@ "animation-type": "discrete", "initial": "none", "values": [ - "none" + "none", + { + "value": "auto", + "status": "non-standard" + }, + { + "value": "match-element", + "status": "non-standard" + } ], "codegen-properties": { "style-builder-converter": "ViewTransitionName", diff --git a/Source/WebCore/css/process-css-properties.py b/Source/WebCore/css/process-css-properties.py index de7509914447b..e55319f674f5b 100755 --- a/Source/WebCore/css/process-css-properties.py +++ b/Source/WebCore/css/process-css-properties.py @@ -1595,7 +1595,6 @@ def perform_fixups(self, all_rules): return all_rules.rules_by_name[name_for_lookup].grammar.root_term.perform_fixups(all_rules) return self - def perform_fixups_for_values_references(self, values): # NOTE: The actual name in the grammar is "<>", which we store as is_internal + 'values'. if self.is_internal and self.name.name == "values": @@ -1610,6 +1609,10 @@ def is_builtin(self): def supported_keywords(self): return set() + @property + def has_non_builtin_reference_terms(self): + return not self.is_builtin + # LiteralTerm represents a direct match of a literal character or string. The # syntax in the CSS specifications is either a bare delimiter character or a @@ -1642,6 +1645,10 @@ def perform_fixups_for_values_references(self, values): def supported_keywords(self): return set() + @property + def has_non_builtin_reference_terms(self): + return False + # KeywordTerm represents a direct keyword match. The syntax in the CSS specifications # is a bare string. @@ -1677,6 +1684,10 @@ def perform_fixups_for_values_references(self, values): def supported_keywords(self): return {self.value.name} + @property + def has_non_builtin_reference_terms(self): + return False + @property def requires_context(self): return self.settings_flag or self.status == "internal" @@ -1773,6 +1784,10 @@ def supported_keywords(self): result.update(term.supported_keywords) return result + @property + def has_non_builtin_reference_terms(self): + return any(term.has_non_builtin_reference_terms for term in self.terms) + # GroupTerm represents matching a list of provided terms with # options for whether the matches are ordered and whether all @@ -1838,6 +1853,10 @@ def supported_keywords(self): result.update(subterm.supported_keywords) return result + @property + def has_non_builtin_reference_terms(self): + return any(term.has_non_builtin_reference_terms for term in self.subterms) + # OptionalTerm represents matching a term that is allowed to # be ommited. The syntax in the CSS specifications uses a @@ -1879,6 +1898,10 @@ def perform_fixups_for_values_references(self, values): def supported_keywords(self): return self.subterm.supported_keywords + @property + def has_non_builtin_reference_terms(self): + return self.subterm.has_non_builtin_reference_terms + # UnboundedRepetitionTerm represents matching a list of terms # separated by either spaces or commas. The syntax in the CSS @@ -1949,6 +1972,10 @@ def perform_fixups_for_values_references(self, values): def supported_keywords(self): return self.repeated_term.supported_keywords + @property + def has_non_builtin_reference_terms(self): + return self.repeated_term.has_non_builtin_reference_terms + # BoundedRepetitionTerm represents matching a list of terms # separated by either spaces or commas where the list of terms @@ -2003,6 +2030,10 @@ def perform_fixups_for_values_references(self, values): def supported_keywords(self): return self.repeated_term.supported_keywords + @property + def has_non_builtin_reference_terms(self): + return self.repeated_term.has_non_builtin_reference_terms + # FixedSizeRepetitionTerm represents matching a list of terms # separated by either spaces or commas where the list of terms @@ -2056,6 +2087,10 @@ def perform_fixups_for_values_references(self, values): def supported_keywords(self): return self.repeated_term.supported_keywords + @property + def has_non_builtin_reference_terms(self): + return self.repeated_term.has_non_builtin_reference_terms + # FunctionTerm represents matching a use of the CSS function call syntax # which provides a way for specifications to differentiate groups by @@ -2092,6 +2127,10 @@ def perform_fixups_for_values_references(self, values): def supported_keywords(self): return self.parameter_group_term.supported_keywords + @property + def has_non_builtin_reference_terms(self): + return self.parameter_group_term.has_non_builtin_reference_terms + # Container for the name and root term for a grammar. Used for both shared rules and property specific grammars. class Grammar: @@ -2118,6 +2157,10 @@ def perform_fixups_for_values_references(self, values): self.root_term = self.root_term.perform_fixups_for_values_references(values) def check_against_values(self, values): + if self.has_non_builtin_reference_terms: + # If the grammar has any non-builtin references, the grammar is incomplete and this check cannot be performed. + return + keywords_supported_by_grammar = self.supported_keywords keywords_listed_as_values = frozenset(value.name for value in values) @@ -2163,6 +2206,10 @@ def fast_path_keyword_terms_sorted_by_name(self): def supported_keywords(self): return self.root_term.supported_keywords + @property + def has_non_builtin_reference_terms(self): + return self.root_term.has_non_builtin_reference_terms + # A shared grammar rule and metadata describing it. Part of the set of rules tracked by SharedGrammarRules. class SharedGrammarRule: From a9baf3a34bde2fea859623aaae6f8893b03d5874 Mon Sep 17 00:00:00 2001 From: Yusuke Suzuki Date: Sun, 23 Feb 2025 18:35:57 -0800 Subject: [PATCH 103/174] [JSC] Remove HandleExceptionWithCallFrameRollback thunk https://bugs.webkit.org/show_bug.cgi?id=288318 rdar://problem/145432961 Reviewed by Yijia Huang. This patch removes HandleExceptionWithCallFrameRollback thunk as it is only used by throwStackOverflow thunk. We just embed it into throwStackOverflow thunk. * Source/JavaScriptCore/dfg/DFGJITCompiler.cpp: (JSC::DFG::JITCompiler::exceptionJumpWithCallFrameRollback): Deleted. * Source/JavaScriptCore/dfg/DFGJITCompiler.h: * Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callThrowOperationWithCallFrameRollback): Deleted. * Source/JavaScriptCore/jit/JIT.cpp: (JSC::JIT::exceptionChecksWithCallFrameRollback): Deleted. * Source/JavaScriptCore/jit/JIT.h: * Source/JavaScriptCore/jit/JITThunks.h: * Source/JavaScriptCore/jit/ThunkGenerators.cpp: (JSC::throwStackOverflowAtPrologueGenerator): (JSC::handleExceptionWithCallFrameRollbackGenerator): Deleted. * Source/JavaScriptCore/jit/ThunkGenerators.h: Canonical link: https://commits.webkit.org/290924@main --- Source/JavaScriptCore/dfg/DFGJITCompiler.cpp | 5 ---- Source/JavaScriptCore/dfg/DFGJITCompiler.h | 2 -- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h | 8 ------- Source/JavaScriptCore/jit/JIT.cpp | 5 ---- Source/JavaScriptCore/jit/JIT.h | 11 --------- Source/JavaScriptCore/jit/JITThunks.h | 1 - Source/JavaScriptCore/jit/ThunkGenerators.cpp | 24 ++++++------------- Source/JavaScriptCore/jit/ThunkGenerators.h | 1 - 8 files changed, 7 insertions(+), 50 deletions(-) diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp index cadde84ce9108..7fae331083ac7 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp @@ -372,11 +372,6 @@ void JITCompiler::disassemble(LinkBuffer& linkBuffer) m_disassembler->reportToProfiler(m_graph.m_plan.compilation(), linkBuffer); } -void JITCompiler::exceptionJumpWithCallFrameRollback() -{ - jumpThunk(CodeLocationLabel(vm().getCTIStub(CommonJITThunkID::HandleExceptionWithCallFrameRollback).retaggedCode())); -} - #if USE(JSVALUE32_64) void* JITCompiler::addressOfDoubleConstant(Node* node) { diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.h b/Source/JavaScriptCore/dfg/DFGJITCompiler.h index a0b4c22bc2844..450e6768aa7fa 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.h +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.h @@ -167,8 +167,6 @@ class JITCompiler : public CCallHelpers { call(address, OperationPtrTag); } - void exceptionJumpWithCallFrameRollback(); - OSRExitCompilationInfo& appendExitInfo(MacroAssembler::JumpList jumpsToFail = MacroAssembler::JumpList()) { OSRExitCompilationInfo info; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 084212562e026..9db9f057b1e87 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -1190,14 +1190,6 @@ class SpeculativeJIT : public JITCompiler { return call; } - JITCompiler::Call callThrowOperationWithCallFrameRollback(V_JITOperation_Cb operation, GPRReg codeBlockGPR) - { - setupArguments(codeBlockGPR); - JITCompiler::Call call = appendCall(operation); - exceptionJumpWithCallFrameRollback(); - return call; - } - void prepareForExternalCall() { #if !defined(NDEBUG) && !CPU(ARM_THUMB2) diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp index 8de49fb999ea0..17c3b32ed0367 100644 --- a/Source/JavaScriptCore/jit/JIT.cpp +++ b/Source/JavaScriptCore/jit/JIT.cpp @@ -1091,11 +1091,6 @@ void JIT::exceptionCheck() exceptionCheck(emitExceptionCheck(vm())); } -void JIT::exceptionChecksWithCallFrameRollback(Jump jumpToHandler) -{ - jumpToHandler.linkThunk(CodeLocationLabel(vm().getCTIStub(CommonJITThunkID::HandleExceptionWithCallFrameRollback).retaggedCode()), this); -} - } // namespace JSC WTF_ALLOW_UNSAFE_BUFFER_USAGE_END diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h index c93b06571f802..2061554605106 100644 --- a/Source/JavaScriptCore/jit/JIT.h +++ b/Source/JavaScriptCore/jit/JIT.h @@ -246,7 +246,6 @@ namespace JSC { void exceptionCheck(Jump jumpToHandler); void exceptionCheck(); - void exceptionChecksWithCallFrameRollback(Jump jumpToHandler); void advanceToNextCheckpoint(); void emitJumpSlowToHotForCheckpoint(Jump); @@ -779,16 +778,6 @@ namespace JSC { return appendCall(operation); } - template - MacroAssembler::Call callThrowOperationWithCallFrameRollback(OperationType operation, Args... args) - { - setupArguments(args...); - updateTopCallFrame(); // The callee is responsible for setting topCallFrame to their caller - MacroAssembler::Call call = appendCall(operation); - exceptionChecksWithCallFrameRollback(jump()); - return call; - } - enum class ProfilingPolicy { ShouldEmitProfiling, NoProfiling diff --git a/Source/JavaScriptCore/jit/JITThunks.h b/Source/JavaScriptCore/jit/JITThunks.h index bb3afd6dd3fbd..e53b7e03a1bd2 100644 --- a/Source/JavaScriptCore/jit/JITThunks.h +++ b/Source/JavaScriptCore/jit/JITThunks.h @@ -54,7 +54,6 @@ class NativeExecutable; // List up super common stubs so that we initialize them eagerly. #define JSC_FOR_EACH_COMMON_THUNK(macro) \ macro(HandleException, handleExceptionGenerator) \ - macro(HandleExceptionWithCallFrameRollback, handleExceptionWithCallFrameRollbackGenerator) \ macro(CheckException, checkExceptionGenerator) \ macro(NativeCall, nativeCallGenerator) \ macro(NativeConstruct, nativeConstructGenerator) \ diff --git a/Source/JavaScriptCore/jit/ThunkGenerators.cpp b/Source/JavaScriptCore/jit/ThunkGenerators.cpp index 164ea6ce7904f..723211e311a99 100644 --- a/Source/JavaScriptCore/jit/ThunkGenerators.cpp +++ b/Source/JavaScriptCore/jit/ThunkGenerators.cpp @@ -59,22 +59,6 @@ MacroAssemblerCodeRef handleExceptionGenerator(VM& vm) return FINALIZE_THUNK(patchBuffer, JITThunkPtrTag, "handleException"_s, "handleException"); } -MacroAssemblerCodeRef handleExceptionWithCallFrameRollbackGenerator(VM& vm) -{ - CCallHelpers jit; - - jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm.topEntryFrame, GPRInfo::argumentGPR0); - - jit.move(CCallHelpers::TrustedImmPtr(&vm), GPRInfo::argumentGPR0); - jit.prepareCallOperation(vm); - CCallHelpers::Call operation = jit.call(OperationPtrTag); - jit.jumpToExceptionHandler(vm); - - LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, LinkBuffer::Profile::ExtraCTIThunk); - patchBuffer.link(operation, operationLookupExceptionHandlerFromCallerFrame); - return FINALIZE_THUNK(patchBuffer, JITThunkPtrTag, "handleExceptionWithCallFrameRollback"_s, "handleExceptionWithCallFrameRollback"); -} - MacroAssemblerCodeRef popThunkStackPreservesAndHandleExceptionGenerator(VM& vm) { CCallHelpers jit; @@ -198,7 +182,13 @@ MacroAssemblerCodeRef throwStackOverflowAtPrologueGenerator(VM& jit.emitGetFromCallFrameHeaderPtr(CallFrameSlot::codeBlock, GPRInfo::argumentGPR0); jit.prepareCallOperation(vm); jit.callOperation(operationThrowStackOverflowError); - jit.jumpThunk(CodeLocationLabel(vm.getCTIStub(CommonJITThunkID::HandleExceptionWithCallFrameRollback).retaggedCode())); + + jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm.topEntryFrame, GPRInfo::argumentGPR0); + + jit.move(CCallHelpers::TrustedImmPtr(&vm), GPRInfo::argumentGPR0); + jit.prepareCallOperation(vm); + jit.callOperation(operationLookupExceptionHandlerFromCallerFrame); + jit.jumpToExceptionHandler(vm); LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, LinkBuffer::Profile::Thunk); return FINALIZE_THUNK(patchBuffer, JITThunkPtrTag, "throwStackOverflow"_s, "throwStackOverflow"); diff --git a/Source/JavaScriptCore/jit/ThunkGenerators.h b/Source/JavaScriptCore/jit/ThunkGenerators.h index 3ee58534d1a9e..d584122bcde24 100644 --- a/Source/JavaScriptCore/jit/ThunkGenerators.h +++ b/Source/JavaScriptCore/jit/ThunkGenerators.h @@ -39,7 +39,6 @@ template class MacroAssemblerCodeRef; class VM; MacroAssemblerCodeRef handleExceptionGenerator(VM&); -MacroAssemblerCodeRef handleExceptionWithCallFrameRollbackGenerator(VM&); MacroAssemblerCodeRef popThunkStackPreservesAndHandleExceptionGenerator(VM&); MacroAssemblerCodeRef throwExceptionFromCallGenerator(VM&); From 6999ecda7ef343927f1bbfa0b346ad0b39eaebb6 Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Sun, 23 Feb 2025 19:02:22 -0800 Subject: [PATCH 104/174] Use a smart pointer for NetworkLoad::m_client https://bugs.webkit.org/show_bug.cgi?id=288314 Reviewed by Darin Adler. * Source/WebKit/NetworkProcess/Downloads/PendingDownload.h: * Source/WebKit/NetworkProcess/NetworkLoad.cpp: (WebKit::NetworkLoad::shouldCaptureExtraNetworkLoadMetrics const): (WebKit::NetworkLoad::isAllowedToAskUserForCredentials const): (WebKit::NetworkLoad::willPerformHTTPRedirection): (WebKit::NetworkLoad::didReceiveChallenge): (WebKit::NetworkLoad::didReceiveInformationalResponse): (WebKit::NetworkLoad::notifyDidReceiveResponse): (WebKit::NetworkLoad::didReceiveData): (WebKit::NetworkLoad::didCompleteWithError): (WebKit::NetworkLoad::didSendData): (WebKit::NetworkLoad::wasBlocked): (WebKit::NetworkLoad::cannotShowURL): (WebKit::NetworkLoad::wasBlockedByRestrictions): (WebKit::NetworkLoad::wasBlockedByDisabledFTP): * Source/WebKit/NetworkProcess/NetworkLoad.h: * Source/WebKit/NetworkProcess/NetworkLoadClient.h: * Source/WebKit/NetworkProcess/NetworkResourceLoader.h: * Source/WebKit/NetworkProcess/PreconnectTask.h: * Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerNavigationPreloader.h: * Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerSoftUpdateLoader.h: * Source/WebKit/NetworkProcess/cache/NetworkCacheSpeculativeLoad.h: Canonical link: https://commits.webkit.org/290925@main --- .../Downloads/PendingDownload.h | 1 + Source/WebKit/NetworkProcess/NetworkLoad.cpp | 30 +++++++++---------- Source/WebKit/NetworkProcess/NetworkLoad.h | 3 +- .../WebKit/NetworkProcess/NetworkLoadClient.h | 5 +++- .../NetworkProcess/NetworkResourceLoader.h | 2 ++ Source/WebKit/NetworkProcess/PreconnectTask.h | 2 ++ .../ServiceWorkerNavigationPreloader.h | 2 +- .../ServiceWorkerSoftUpdateLoader.h | 2 +- .../cache/NetworkCacheSpeculativeLoad.h | 1 + 9 files changed, 29 insertions(+), 19 deletions(-) diff --git a/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h b/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h index 5521e81b2b8f3..0f84f2511d8ab 100644 --- a/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h +++ b/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h @@ -53,6 +53,7 @@ class NetworkSession; class PendingDownload : public RefCountedAndCanMakeWeakPtr, public NetworkLoadClient, public IPC::MessageSender { WTF_MAKE_TZONE_ALLOCATED(PendingDownload); + WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(PendingDownload); public: static Ref create(IPC::Connection* connection, NetworkLoadParameters&& networkLoadParameters, DownloadID downloadID, NetworkSession& networkSession, const String& suggestedName, WebCore::FromDownloadAttribute fromDownloadAttribute, std::optional webProcessId) { diff --git a/Source/WebKit/NetworkProcess/NetworkLoad.cpp b/Source/WebKit/NetworkProcess/NetworkLoad.cpp index 8c8e04218c880..dd97a099b4457 100644 --- a/Source/WebKit/NetworkProcess/NetworkLoad.cpp +++ b/Source/WebKit/NetworkProcess/NetworkLoad.cpp @@ -135,12 +135,12 @@ void NetworkLoad::reprioritizeRequest(ResourceLoadPriority priority) bool NetworkLoad::shouldCaptureExtraNetworkLoadMetrics() const { - return m_client.get().shouldCaptureExtraNetworkLoadMetrics(); + return m_client->shouldCaptureExtraNetworkLoadMetrics(); } bool NetworkLoad::isAllowedToAskUserForCredentials() const { - return m_client.get().isAllowedToAskUserForCredentials(); + return m_client->isAllowedToAskUserForCredentials(); } void NetworkLoad::convertTaskToDownload(PendingDownload& pendingDownload, const ResourceRequest& updatedRequest, const ResourceResponse& response, ResponseCompletionHandler&& completionHandler) @@ -198,7 +198,7 @@ void NetworkLoad::willPerformHTTPRedirection(ResourceResponse&& redirectResponse request.setRequester(oldRequest.requester()); m_currentRequest = request; - m_client.get().willSendRedirectedRequest(WTFMove(oldRequest), WTFMove(request), WTFMove(redirectResponse), [weakThis = WeakPtr { *this }, completionHandler = WTFMove(completionHandler)] (ResourceRequest&& newRequest) mutable { + m_client->willSendRedirectedRequest(WTFMove(oldRequest), WTFMove(request), WTFMove(redirectResponse), [weakThis = WeakPtr { *this }, completionHandler = WTFMove(completionHandler)] (ResourceRequest&& newRequest) mutable { RefPtr protectedThis = weakThis.get(); if (!protectedThis) return completionHandler({ }); @@ -215,13 +215,13 @@ void NetworkLoad::willPerformHTTPRedirection(ResourceResponse&& redirectResponse void NetworkLoad::didReceiveChallenge(AuthenticationChallenge&& challenge, NegotiatedLegacyTLS negotiatedLegacyTLS, ChallengeCompletionHandler&& completionHandler) { - m_client.get().didReceiveChallenge(challenge); + m_client->didReceiveChallenge(challenge); auto scheme = challenge.protectionSpace().authenticationScheme(); bool isTLSHandshake = scheme == ProtectionSpace::AuthenticationScheme::ServerTrustEvaluationRequested || scheme == ProtectionSpace::AuthenticationScheme::ClientCertificateRequested; if (!isAllowedToAskUserForCredentials() && !isTLSHandshake && !challenge.protectionSpace().isProxy()) { - m_client.get().didBlockAuthenticationChallenge(); + m_client->didBlockAuthenticationChallenge(); completionHandler(AuthenticationChallengeDisposition::UseCredential, { }); return; } @@ -234,7 +234,7 @@ void NetworkLoad::didReceiveChallenge(AuthenticationChallenge&& challenge, Negot void NetworkLoad::didReceiveInformationalResponse(ResourceResponse&& response) { - m_client.get().didReceiveInformationalResponse(WTFMove(response)); + m_client->didReceiveInformationalResponse(WTFMove(response)); } void NetworkLoad::didReceiveResponse(ResourceResponse&& response, NegotiatedLegacyTLS negotiatedLegacyTLS, PrivateRelayed privateRelayed, ResponseCompletionHandler&& completionHandler) @@ -268,13 +268,13 @@ void NetworkLoad::notifyDidReceiveResponse(ResourceResponse&& response, Negotiat response.includeCertificateInfo(auditToken); } - m_client.get().didReceiveResponse(WTFMove(response), privateRelayed, WTFMove(completionHandler)); + m_client->didReceiveResponse(WTFMove(response), privateRelayed, WTFMove(completionHandler)); } void NetworkLoad::didReceiveData(const WebCore::SharedBuffer& buffer) { // FIXME: This should be the encoded data length, not the decoded data length. - m_client.get().didReceiveBuffer(buffer, buffer.size()); + m_client->didReceiveBuffer(buffer, buffer.size()); } void NetworkLoad::didCompleteWithError(const ResourceError& error, const WebCore::NetworkLoadMetrics& networkLoadMetrics) @@ -283,34 +283,34 @@ void NetworkLoad::didCompleteWithError(const ResourceError& error, const WebCore scheduler->unschedule(*this, &networkLoadMetrics); if (error.isNull()) - m_client.get().didFinishLoading(networkLoadMetrics); + m_client->didFinishLoading(networkLoadMetrics); else - m_client.get().didFailLoading(error); + m_client->didFailLoading(error); } void NetworkLoad::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend) { - m_client.get().didSendData(totalBytesSent, totalBytesExpectedToSend); + m_client->didSendData(totalBytesSent, totalBytesExpectedToSend); } void NetworkLoad::wasBlocked() { - m_client.get().didFailLoading(blockedError(m_currentRequest)); + m_client->didFailLoading(blockedError(m_currentRequest)); } void NetworkLoad::cannotShowURL() { - m_client.get().didFailLoading(cannotShowURLError(m_currentRequest)); + m_client->didFailLoading(cannotShowURLError(m_currentRequest)); } void NetworkLoad::wasBlockedByRestrictions() { - m_client.get().didFailLoading(wasBlockedByRestrictionsError(m_currentRequest)); + m_client->didFailLoading(wasBlockedByRestrictionsError(m_currentRequest)); } void NetworkLoad::wasBlockedByDisabledFTP() { - m_client.get().didFailLoading(ftpDisabledError(m_currentRequest)); + m_client->didFailLoading(ftpDisabledError(m_currentRequest)); } void NetworkLoad::didNegotiateModernTLS(const URL& url) diff --git a/Source/WebKit/NetworkProcess/NetworkLoad.h b/Source/WebKit/NetworkProcess/NetworkLoad.h index c88980a70d258..93fa72068b6eb 100644 --- a/Source/WebKit/NetworkProcess/NetworkLoad.h +++ b/Source/WebKit/NetworkProcess/NetworkLoad.h @@ -29,6 +29,7 @@ #include "NetworkDataTask.h" #include "NetworkLoadParameters.h" #include "NetworkSession.h" +#include #include #include @@ -120,7 +121,7 @@ class NetworkLoad final : public RefCounted, public NetworkDataTask void notifyDidReceiveResponse(WebCore::ResourceResponse&&, NegotiatedLegacyTLS, PrivateRelayed, ResponseCompletionHandler&&); - std::reference_wrapper m_client; + CheckedRef m_client; const Ref m_networkProcess; const NetworkLoadParameters m_parameters; RefPtr m_task; diff --git a/Source/WebKit/NetworkProcess/NetworkLoadClient.h b/Source/WebKit/NetworkProcess/NetworkLoadClient.h index 6d6eb4e2b4973..be5a0601b0171 100644 --- a/Source/WebKit/NetworkProcess/NetworkLoadClient.h +++ b/Source/WebKit/NetworkProcess/NetworkLoadClient.h @@ -25,6 +25,7 @@ #pragma once +#include #include namespace WebCore { @@ -42,7 +43,9 @@ namespace WebKit { enum class PrivateRelayed : bool; using ResponseCompletionHandler = CompletionHandler; -class NetworkLoadClient { +class NetworkLoadClient : public CanMakeThreadSafeCheckedPtr { + WTF_MAKE_FAST_ALLOCATED; + WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(NetworkLoadClient); public: virtual ~NetworkLoadClient() { } diff --git a/Source/WebKit/NetworkProcess/NetworkResourceLoader.h b/Source/WebKit/NetworkProcess/NetworkResourceLoader.h index 95ff2f7d1e7a3..1db3250722b33 100644 --- a/Source/WebKit/NetworkProcess/NetworkResourceLoader.h +++ b/Source/WebKit/NetworkProcess/NetworkResourceLoader.h @@ -87,6 +87,8 @@ class NetworkResourceLoader final , public CanMakeWeakPtr #endif , public WebCore::ReportingClient { + WTF_MAKE_FAST_ALLOCATED; + WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(NetworkResourceLoader); public: #if ENABLE(CONTENT_FILTERING) void ref() const final { RefCounted::ref(); } diff --git a/Source/WebKit/NetworkProcess/PreconnectTask.h b/Source/WebKit/NetworkProcess/PreconnectTask.h index f44975296ce0f..d5090aa7c315e 100644 --- a/Source/WebKit/NetworkProcess/PreconnectTask.h +++ b/Source/WebKit/NetworkProcess/PreconnectTask.h @@ -40,6 +40,8 @@ class NetworkProcess; class NetworkSession; class PreconnectTask final : public NetworkLoadClient { + WTF_MAKE_FAST_ALLOCATED; + WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(PreconnectTask); public: PreconnectTask(NetworkSession&, NetworkLoadParameters&&, CompletionHandler&&); ~PreconnectTask(); diff --git a/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerNavigationPreloader.h b/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerNavigationPreloader.h index 5d45b70dfa84a..63817438193b3 100644 --- a/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerNavigationPreloader.h +++ b/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerNavigationPreloader.h @@ -45,7 +45,7 @@ class DownloadManager; class NetworkLoad; class NetworkSession; -class ServiceWorkerNavigationPreloader final : public NetworkLoadClient, public CanMakeWeakPtr, public CanMakeCheckedPtr { +class ServiceWorkerNavigationPreloader final : public NetworkLoadClient, public CanMakeWeakPtr { WTF_MAKE_TZONE_ALLOCATED(ServiceWorkerNavigationPreloader); WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(ServiceWorkerNavigationPreloader); public: diff --git a/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerSoftUpdateLoader.h b/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerSoftUpdateLoader.h index 7dbd065e3beab..1de612835c227 100644 --- a/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerSoftUpdateLoader.h +++ b/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerSoftUpdateLoader.h @@ -47,7 +47,7 @@ namespace WebKit { class NetworkLoad; class NetworkSession; -class ServiceWorkerSoftUpdateLoader final : public NetworkLoadClient, public CanMakeWeakPtr, public CanMakeCheckedPtr { +class ServiceWorkerSoftUpdateLoader final : public NetworkLoadClient, public CanMakeWeakPtr { WTF_MAKE_TZONE_ALLOCATED(ServiceWorkerSoftUpdateLoader); WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(ServiceWorkerSoftUpdateLoader); public: diff --git a/Source/WebKit/NetworkProcess/cache/NetworkCacheSpeculativeLoad.h b/Source/WebKit/NetworkProcess/cache/NetworkCacheSpeculativeLoad.h index 12f19ad34044c..ce081a9d847a2 100644 --- a/Source/WebKit/NetworkProcess/cache/NetworkCacheSpeculativeLoad.h +++ b/Source/WebKit/NetworkProcess/cache/NetworkCacheSpeculativeLoad.h @@ -50,6 +50,7 @@ namespace NetworkCache { class SpeculativeLoad final : public NetworkLoadClient { WTF_MAKE_TZONE_ALLOCATED(SpeculativeLoad); + WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(SpeculativeLoad); public: using RevalidationCompletionHandler = CompletionHandler)>; SpeculativeLoad(Cache&, const GlobalFrameID&, const WebCore::ResourceRequest&, std::unique_ptr, std::optional, bool allowPrivacyProxy, OptionSet, RevalidationCompletionHandler&&); From 1392e7f27baf747b79b06bf132b7ba58e60778ab Mon Sep 17 00:00:00 2001 From: Chris Dumez Date: Sun, 23 Feb 2025 19:21:44 -0800 Subject: [PATCH 105/174] Address safer cpp failures in RenderBlock https://bugs.webkit.org/show_bug.cgi?id=288313 Reviewed by Alan Baradlay. * Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations: * Source/WebCore/SaferCPPExpectations/UncheckedLocalVarsCheckerExpectations: * Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations: * Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations: * Source/WebCore/rendering/RenderBlock.cpp: (WebCore::OverflowEventDispatcher::~OverflowEventDispatcher): (WebCore::RenderBlock::clone const): (WebCore::RenderBlock::deleteLines): (WebCore::RenderBlock::paint): (WebCore::RenderBlock::paintCaret): (WebCore::RenderBlock::paintObject): (WebCore::RenderBlock::establishesIndependentFormattingContextIgnoringDisplayType const): (WebCore::RenderBlock::isSelectionRoot const): (WebCore::RenderBlock::blockSelectionGap): (WebCore::RenderBlock::logicalLeftSelectionGap): (WebCore::RenderBlock::logicalRightSelectionGap): (WebCore::RenderBlock::isPointInOverflowControl): (WebCore::RenderBlock::protectedNonPseudoElement const): (WebCore::isEditingBoundary): (WebCore::positionForPointRespectingEditingBoundaries): (WebCore::RenderBlock::hasLineIfEmpty const): (WebCore::RenderBlock::baselinePosition const): * Source/WebCore/rendering/RenderBlock.h: (WebCore::RenderBlock::createAnonymousWithParentRendererAndDisplay): (WebCore::RenderBlock::createAnonymousBoxWithSameTypeAs const): (WebCore::RenderBlock::createAnonymousBlock const): * Source/WebCore/rendering/RenderElement.h: * Source/WebCore/rendering/RenderLayer.cpp: (WebCore::RenderLayer::calculateClipRects const): * Source/WebCore/rendering/RenderLayer.h: * Source/WebCore/rendering/RenderObject.h: (WebCore::RenderObject::protectedNonPseudoNode const): * Source/WebCore/rendering/RenderTextControl.cpp: (WebCore::RenderTextControl::protectedTextFormControlElement const): * Source/WebCore/rendering/RenderTextControl.h: Canonical link: https://commits.webkit.org/290926@main --- .../UncheckedCallArgsCheckerExpectations | 1 - .../UncheckedLocalVarsCheckerExpectations | 1 - .../UncountedCallArgsCheckerExpectations | 2 - .../UncountedLocalVarsCheckerExpectations | 1 - Source/WebCore/rendering/RenderBlock.cpp | 70 +++++++++---------- Source/WebCore/rendering/RenderBlock.h | 15 ---- Source/WebCore/rendering/RenderBlockInlines.h | 16 +++++ Source/WebCore/rendering/RenderElement.h | 1 + Source/WebCore/rendering/RenderLayer.cpp | 5 ++ Source/WebCore/rendering/RenderLayer.h | 1 + Source/WebCore/rendering/RenderObject.h | 1 + .../WebCore/rendering/RenderTextControl.cpp | 5 ++ Source/WebCore/rendering/RenderTextControl.h | 1 + .../updating/RenderTreeBuilderBlock.cpp | 1 + .../RenderTreeBuilderFormControls.cpp | 1 + 15 files changed, 67 insertions(+), 55 deletions(-) diff --git a/Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations index 3a986347f7605..0412c94e7abe6 100644 --- a/Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations @@ -67,7 +67,6 @@ platform/mac/ScrollbarsControllerMac.mm platform/mac/VideoPresentationInterfaceMac.mm rendering/BackgroundPainter.cpp rendering/InlineBoxPainter.cpp -rendering/RenderBlock.cpp rendering/RenderBox.cpp rendering/RenderBoxModelObject.cpp rendering/RenderElement.cpp diff --git a/Source/WebCore/SaferCPPExpectations/UncheckedLocalVarsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncheckedLocalVarsCheckerExpectations index 39370d775f7dc..ca52740d57180 100644 --- a/Source/WebCore/SaferCPPExpectations/UncheckedLocalVarsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncheckedLocalVarsCheckerExpectations @@ -57,7 +57,6 @@ platform/mac/VideoPresentationInterfaceMac.mm platform/mac/WebPlaybackControlsManager.mm rendering/AccessibilityRegionContext.cpp rendering/BackgroundPainter.cpp -rendering/RenderBlock.cpp rendering/RenderBox.cpp rendering/RenderBoxModelObject.cpp rendering/RenderLayer.cpp diff --git a/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations index fa7fee285987f..9dd9c4a0f901a 100644 --- a/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations @@ -1160,8 +1160,6 @@ rendering/LegacyLineLayout.cpp rendering/MarkedText.cpp rendering/MotionPath.cpp rendering/RenderAttachment.cpp -rendering/RenderBlock.cpp -rendering/RenderBlock.h rendering/RenderBox.cpp rendering/RenderBoxModelObject.cpp rendering/RenderButton.cpp diff --git a/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations b/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations index 16b3179ffa68b..67af62ef3369d 100644 --- a/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations +++ b/Source/WebCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations @@ -636,7 +636,6 @@ rendering/MarkedText.cpp rendering/MotionPath.cpp rendering/ReferencedSVGResources.cpp rendering/RenderAttachment.cpp -rendering/RenderBlock.cpp rendering/RenderBox.cpp rendering/RenderCounter.cpp rendering/RenderElement.cpp diff --git a/Source/WebCore/rendering/RenderBlock.cpp b/Source/WebCore/rendering/RenderBlock.cpp index cecf62dea2009..bb82d47879b58 100644 --- a/Source/WebCore/rendering/RenderBlock.cpp +++ b/Source/WebCore/rendering/RenderBlock.cpp @@ -296,11 +296,11 @@ class OverflowEventDispatcher { Ref overflowEvent = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow); overflowEvent->setTarget(RefPtr { m_block->element() }); - m_block->document().enqueueOverflowEvent(WTFMove(overflowEvent)); + m_block->protectedDocument()->enqueueOverflowEvent(WTFMove(overflowEvent)); } private: - const RenderBlock* m_block; + CheckedPtr m_block; bool m_shouldDispatchEvent; bool m_hadHorizontalLayoutOverflow; bool m_hadVerticalLayoutOverflow; @@ -435,7 +435,7 @@ RenderPtr RenderBlock::clone() const cloneBlock->setChildrenInline(childrenInline()); } else { RenderTreePosition insertionPosition(*parent()); - cloneBlock = static_pointer_cast(element()->createElementRenderer(RenderStyle::clone(style()), insertionPosition)); + cloneBlock = static_pointer_cast(protectedElement()->createElementRenderer(RenderStyle::clone(style()), insertionPosition)); cloneBlock->initializeStyle(); // This takes care of setting the right value of childrenInline in case @@ -449,8 +449,8 @@ RenderPtr RenderBlock::clone() const void RenderBlock::deleteLines() { - if (AXObjectCache* cache = document().existingAXObjectCache()) - cache->deferRecomputeIsIgnored(element()); + if (AXObjectCache* cache = protectedDocument()->existingAXObjectCache()) + cache->deferRecomputeIsIgnored(protectedElement().get()); } bool RenderBlock::childrenPreventSelfCollapsing() const @@ -1081,9 +1081,13 @@ void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with // z-index. We paint after we painted the background/border, so that the scrollbars will // sit above the background/border. - if ((phase == PaintPhase::BlockBackground || phase == PaintPhase::ChildBlockBackground) && hasNonVisibleOverflow() && layer() - && layer()->scrollableArea() && style().usedVisibility() == Visibility::Visible && paintInfo.shouldPaintWithinRoot(*this) && !paintInfo.paintRootBackgroundOnly()) - layer()->scrollableArea()->paintOverflowControls(paintInfo.context(), roundedIntPoint(adjustedPaintOffset), snappedIntRect(paintInfo.rect)); + if (phase == PaintPhase::BlockBackground || phase == PaintPhase::ChildBlockBackground) { + CheckedPtr layer = this->layer(); + if (hasNonVisibleOverflow() && layer && layer->scrollableArea() && style().usedVisibility() == Visibility::Visible + && paintInfo.shouldPaintWithinRoot(*this) && !paintInfo.paintRootBackgroundOnly()) { + layer->checkedScrollableArea()->paintOverflowControls(paintInfo.context(), roundedIntPoint(adjustedPaintOffset), snappedIntRect(paintInfo.rect)); + } + } } void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) @@ -1194,7 +1198,7 @@ void RenderBlock::paintCaret(PaintInfo& paintInfo, const LayoutPoint& paintOffse bool isContentEditable = page().dragCaretController().isContentEditable(); if (shouldPaintCaret(caretPainter, isContentEditable)) - page().dragCaretController().paintDragCaret(&frame(), paintInfo.context(), paintOffset); + page().dragCaretController().paintDragCaret(protectedFrame().ptr(), paintInfo.context(), paintOffset); break; } @@ -1258,11 +1262,12 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs if (paintPhase == PaintPhase::EventRegion) { auto borderRect = LayoutRect(paintOffset, size()); + Ref document = this->document(); if (paintInfo.paintBehavior.contains(PaintBehavior::EventRegionIncludeBackground) && visibleToHitTesting()) { auto borderShape = BorderShape::shapeForBorderRect(style(), borderRect); LOG_WITH_STREAM(EventRegions, stream << "RenderBlock " << *this << " uniting region " << borderShape.deprecatedRoundedRect() << " event listener types " << style().eventListenerRegionTypes()); - bool overrideUserModifyIsEditable = isRenderTextControl() && downcast(*this).textFormControlElement().isInnerTextElementEditable(); - paintInfo.eventRegionContext()->unite(borderShape.deprecatedPixelSnappedRoundedRect(document().deviceScaleFactor()), *this, style(), overrideUserModifyIsEditable); + bool overrideUserModifyIsEditable = isRenderTextControl() && downcast(*this).protectedTextFormControlElement()->isInnerTextElementEditable(); + paintInfo.eventRegionContext()->unite(borderShape.deprecatedPixelSnappedRoundedRect(document->deviceScaleFactor()), *this, style(), overrideUserModifyIsEditable); } if (!paintInfo.paintBehavior.contains(PaintBehavior::EventRegionIncludeForeground)) @@ -1272,16 +1277,16 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs LOG_WITH_STREAM(EventRegions, stream << "RenderBlock " << *this << " needsTraverseDescendants for event region: hasVisualOverflow: " << hasVisualOverflow() << " containsFloats: " << containsFloats() << " border box is outside current region: " << !paintInfo.eventRegionContext()->contains(enclosingIntRect(borderRect)) << " needsEventRegionUpdateForNonCompositedFrame:" << view().needsEventRegionUpdateForNonCompositedFrame()); #if ENABLE(TOUCH_ACTION_REGIONS) - needsTraverseDescendants |= document().mayHaveElementsWithNonAutoTouchAction(); - LOG_WITH_STREAM(EventRegions, stream << " may have touch-action elements: " << document().mayHaveElementsWithNonAutoTouchAction()); + needsTraverseDescendants |= document->mayHaveElementsWithNonAutoTouchAction(); + LOG_WITH_STREAM(EventRegions, stream << " may have touch-action elements: " << document->mayHaveElementsWithNonAutoTouchAction()); #endif #if ENABLE(WHEEL_EVENT_REGIONS) - needsTraverseDescendants |= document().hasWheelEventHandlers(); - LOG_WITH_STREAM(EventRegions, stream << " has wheel event handlers: " << document().hasWheelEventHandlers()); + needsTraverseDescendants |= document->hasWheelEventHandlers(); + LOG_WITH_STREAM(EventRegions, stream << " has wheel event handlers: " << document->hasWheelEventHandlers()); #endif #if ENABLE(TOUCH_EVENT_REGIONS) - needsTraverseDescendants |= document().hasTouchEventHandlers(); - LOG_WITH_STREAM(EventRegions, stream << " has touch event handlers: " << document().hasTouchEventHandlers()); + needsTraverseDescendants |= document->hasTouchEventHandlers(); + LOG_WITH_STREAM(EventRegions, stream << " has touch event handlers: " << document->hasTouchEventHandlers()); #endif #if ENABLE(EDITABLE_REGION) @@ -1289,8 +1294,8 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs // though it's actually the inner text element of the control that is editable. // So, no need to traverse to find the inner text element in this case. if (!isRenderTextControl()) { - needsTraverseDescendants |= document().mayHaveEditableElements() && page().shouldBuildEditableRegion(); - LOG_WITH_STREAM(EventRegions, stream << " needs editable event region: " << (document().mayHaveEditableElements() && page().shouldBuildEditableRegion())); + needsTraverseDescendants |= document->mayHaveEditableElements() && page().shouldBuildEditableRegion(); + LOG_WITH_STREAM(EventRegions, stream << " needs editable event region: " << (document->mayHaveEditableElements() && page().shouldBuildEditableRegion())); } #endif @@ -1413,7 +1418,7 @@ bool RenderBlock::establishesIndependentFormattingContextIgnoringDisplayType(con || isBlockBoxWithPotentiallyScrollableOverflow() || style.containsLayout() || style.containerType() != ContainerType::Normal - || WebCore::shouldApplyPaintContainment(style, *element()) + || WebCore::shouldApplyPaintContainment(style, *protectedElement()) || (style.isDisplayBlockLevel() && style.blockStepSize()); } @@ -1515,7 +1520,7 @@ bool RenderBlock::isSelectionRoot() const return true; if (view().selection().start()) { - Node* startElement = view().selection().start()->node(); + RefPtr startElement = view().selection().start()->node(); if (startElement && startElement->rootEditableElement() == element()) return true; } @@ -1745,7 +1750,7 @@ LayoutRect RenderBlock::blockSelectionGap(RenderBlock& rootBlock, const LayoutPo LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight)); if (paintInfo) - paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, document().deviceScaleFactor()), selectionBackgroundColor()); + paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, protectedDocument()->deviceScaleFactor()), selectionBackgroundColor()); return gapRect; } @@ -1761,7 +1766,7 @@ LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock& rootBlock, const La LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); if (paintInfo) - paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, document().deviceScaleFactor()), selObj->selectionBackgroundColor()); + paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, protectedDocument()->deviceScaleFactor()), selObj->selectionBackgroundColor()); return gapRect; } @@ -1777,7 +1782,7 @@ LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock& rootBlock, const L LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); if (paintInfo) - paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, document().deviceScaleFactor()), selObj->selectionBackgroundColor()); + paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, protectedDocument()->deviceScaleFactor()), selObj->selectionBackgroundColor()); return gapRect; } @@ -2073,7 +2078,7 @@ bool RenderBlock::isPointInOverflowControl(HitTestResult& result, const LayoutPo { if (!scrollsOverflow()) return false; - if (auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr) + if (CheckedPtr scrollableArea = layer() ? layer()->scrollableArea() : nullptr) return scrollableArea->hitTestOverflowControls(result, roundedIntPoint(locationInContainer - toLayoutSize(accumulatedOffset))); return false; } @@ -2193,7 +2198,7 @@ static inline bool isEditingBoundary(RenderElement* ancestor, RenderObject& chil ASSERT(!ancestor || ancestor->nonPseudoElement()); ASSERT(child.nonPseudoNode()); return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor->parent()->isRenderView()) - || ancestor->nonPseudoElement()->hasEditableStyle() == child.nonPseudoNode()->hasEditableStyle(); + || ancestor->protectedNonPseudoElement()->hasEditableStyle() == child.protectedNonPseudoNode()->hasEditableStyle(); } // FIXME: This function should go on RenderObject as an instance method. Then @@ -2209,7 +2214,7 @@ VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock& parent, LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation)); // If this is an anonymous renderer, we just recur normally - Element* childElement= child.nonPseudoElement(); + RefPtr childElement= child.nonPseudoElement(); if (!childElement) return child.positionForPoint(pointInChildCoordinates, source, nullptr); @@ -2523,13 +2528,8 @@ void RenderBlock::computeChildPreferredLogicalWidths(RenderBox& childBox, Layout bool RenderBlock::hasLineIfEmpty() const { - if (!element()) - return false; - - if (element()->isRootEditableElement()) - return true; - - return false; + RefPtr element = this->element(); + return element && element->isRootEditableElement(); } LayoutUnit RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const @@ -2568,7 +2568,7 @@ LayoutUnit RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLi if (isWritingModeRoot()) return true; - auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr; + CheckedPtr scrollableArea = layer() ? layer()->scrollableArea() : nullptr; if (!scrollableArea) return false; diff --git a/Source/WebCore/rendering/RenderBlock.h b/Source/WebCore/rendering/RenderBlock.h index 59f510c3bbad3..7e8accf41edeb 100644 --- a/Source/WebCore/rendering/RenderBlock.h +++ b/Source/WebCore/rendering/RenderBlock.h @@ -457,21 +457,6 @@ LayoutUnit blockDirectionOffset(RenderBlock& rootBlock, const LayoutSize& offset LayoutUnit inlineDirectionOffset(RenderBlock& rootBlock, const LayoutSize& offsetFromRootBlock); VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock&, RenderBox&, const LayoutPoint&, HitTestSource); -inline RenderPtr RenderBlock::createAnonymousWithParentRendererAndDisplay(const RenderBox& parent, DisplayType display) -{ - return createAnonymousBlockWithStyleAndDisplay(parent.document(), parent.style(), display); -} - -inline RenderPtr RenderBlock::createAnonymousBoxWithSameTypeAs(const RenderBox& renderer) const -{ - return createAnonymousBlockWithStyleAndDisplay(document(), renderer.style(), style().display()); -} - -inline RenderPtr RenderBlock::createAnonymousBlock(DisplayType display) const -{ - return createAnonymousBlockWithStyleAndDisplay(document(), style(), display); -} - } // namespace WebCore SPECIALIZE_TYPE_TRAITS_RENDER_OBJECT(RenderBlock, isRenderBlock()) diff --git a/Source/WebCore/rendering/RenderBlockInlines.h b/Source/WebCore/rendering/RenderBlockInlines.h index 39f259f0d982a..d828b3b72a1c5 100644 --- a/Source/WebCore/rendering/RenderBlockInlines.h +++ b/Source/WebCore/rendering/RenderBlockInlines.h @@ -20,6 +20,7 @@ #pragma once #include "RenderBlock.h" +#include "RenderObjectInlines.h" #include "RenderStyleInlines.h" namespace WebCore { @@ -46,6 +47,21 @@ inline LayoutUnit RenderBlock::startOffsetForLine(LayoutUnit position, LayoutUni : logicalWidth() - logicalRightOffsetForLine(position, logicalHeight); } +inline RenderPtr RenderBlock::createAnonymousWithParentRendererAndDisplay(const RenderBox& parent, DisplayType display) +{ + return createAnonymousBlockWithStyleAndDisplay(parent.protectedDocument(), parent.style(), display); +} + +inline RenderPtr RenderBlock::createAnonymousBoxWithSameTypeAs(const RenderBox& renderer) const +{ + return createAnonymousBlockWithStyleAndDisplay(protectedDocument(), renderer.style(), style().display()); +} + +inline RenderPtr RenderBlock::createAnonymousBlock(DisplayType display) const +{ + return createAnonymousBlockWithStyleAndDisplay(protectedDocument(), style(), display); +} + // Versions that can compute line offsets with the fragment and page offset passed in. Used for speed to avoid having to // compute the fragment all over again when you already know it. inline LayoutUnit RenderBlock::availableLogicalWidthForLine(LayoutUnit position, LayoutUnit logicalHeight) const diff --git a/Source/WebCore/rendering/RenderElement.h b/Source/WebCore/rendering/RenderElement.h index 837951148cdd5..44364afa4386e 100644 --- a/Source/WebCore/rendering/RenderElement.h +++ b/Source/WebCore/rendering/RenderElement.h @@ -87,6 +87,7 @@ class RenderElement : public RenderObject { Element* element() const { return downcast(RenderObject::node()); } RefPtr protectedElement() const { return element(); } Element* nonPseudoElement() const { return downcast(RenderObject::nonPseudoNode()); } + RefPtr protectedNonPseudoElement() const { return nonPseudoElement(); } Element* generatingElement() const; RenderObject* firstChild() const { return m_firstChild.get(); } diff --git a/Source/WebCore/rendering/RenderLayer.cpp b/Source/WebCore/rendering/RenderLayer.cpp index 70e09cbbfe41c..1692b07b3be42 100644 --- a/Source/WebCore/rendering/RenderLayer.cpp +++ b/Source/WebCore/rendering/RenderLayer.cpp @@ -6291,6 +6291,11 @@ RenderLayerScrollableArea* RenderLayer::scrollableArea() const return m_scrollableArea.get(); } +CheckedPtr RenderLayer::checkedScrollableArea() const +{ + return scrollableArea(); +} + #if ENABLE(ASYNC_SCROLLING) && !LOG_DISABLED static TextStream& operator<<(TextStream& ts, RenderLayer::EventRegionInvalidationReason reason) { diff --git a/Source/WebCore/rendering/RenderLayer.h b/Source/WebCore/rendering/RenderLayer.h index 8fa0a36e1b8b7..9300877df48da 100644 --- a/Source/WebCore/rendering/RenderLayer.h +++ b/Source/WebCore/rendering/RenderLayer.h @@ -168,6 +168,7 @@ class RenderLayer final : public CanMakeSingleThreadWeakPtr, public ~RenderLayer(); WEBCORE_EXPORT RenderLayerScrollableArea* scrollableArea() const; + WEBCORE_EXPORT CheckedPtr checkedScrollableArea() const; WEBCORE_EXPORT RenderLayerScrollableArea* ensureLayerScrollableArea(); String name() const; diff --git a/Source/WebCore/rendering/RenderObject.h b/Source/WebCore/rendering/RenderObject.h index b81b21937e25f..af83279b4762d 100644 --- a/Source/WebCore/rendering/RenderObject.h +++ b/Source/WebCore/rendering/RenderObject.h @@ -752,6 +752,7 @@ class RenderObject : public CachedImageClient { RefPtr protectedNode() const { return node(); } Node* nonPseudoNode() const { return isPseudoElement() ? nullptr : node(); } + RefPtr protectedNonPseudoNode() const { return nonPseudoNode(); } // Returns the styled node that caused the generation of this renderer. // This is the same as node() except for renderers of :before and :after diff --git a/Source/WebCore/rendering/RenderTextControl.cpp b/Source/WebCore/rendering/RenderTextControl.cpp index b92baf84effc5..015f1d0d829c0 100644 --- a/Source/WebCore/rendering/RenderTextControl.cpp +++ b/Source/WebCore/rendering/RenderTextControl.cpp @@ -56,6 +56,11 @@ HTMLTextFormControlElement& RenderTextControl::textFormControlElement() const return downcast(nodeForNonAnonymous()); } +Ref RenderTextControl::protectedTextFormControlElement() const +{ + return textFormControlElement(); +} + RefPtr RenderTextControl::innerTextElement() const { return textFormControlElement().innerTextElement(); diff --git a/Source/WebCore/rendering/RenderTextControl.h b/Source/WebCore/rendering/RenderTextControl.h index aba17f8ec165c..faf34133df0bf 100644 --- a/Source/WebCore/rendering/RenderTextControl.h +++ b/Source/WebCore/rendering/RenderTextControl.h @@ -36,6 +36,7 @@ class RenderTextControl : public RenderBlockFlow { virtual ~RenderTextControl(); WEBCORE_EXPORT HTMLTextFormControlElement& textFormControlElement() const; + WEBCORE_EXPORT Ref protectedTextFormControlElement() const; #if PLATFORM(IOS_FAMILY) bool canScroll() const; diff --git a/Source/WebCore/rendering/updating/RenderTreeBuilderBlock.cpp b/Source/WebCore/rendering/updating/RenderTreeBuilderBlock.cpp index fed5c145936d7..cf5fe27a72f3e 100644 --- a/Source/WebCore/rendering/updating/RenderTreeBuilderBlock.cpp +++ b/Source/WebCore/rendering/updating/RenderTreeBuilderBlock.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "RenderTreeBuilderBlock.h" +#include "RenderBlockInlines.h" #include "RenderButton.h" #include "RenderChildIterator.h" #include "RenderMultiColumnFlow.h" diff --git a/Source/WebCore/rendering/updating/RenderTreeBuilderFormControls.cpp b/Source/WebCore/rendering/updating/RenderTreeBuilderFormControls.cpp index cbf6bff9fe501..264e4223fa1a4 100644 --- a/Source/WebCore/rendering/updating/RenderTreeBuilderFormControls.cpp +++ b/Source/WebCore/rendering/updating/RenderTreeBuilderFormControls.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "RenderTreeBuilderFormControls.h" +#include "RenderBlockInlines.h" #include "RenderButton.h" #include "RenderMenuList.h" #include "RenderTreeBuilderBlock.h" From 97eba3acfe770439b967d2ce82b27c27d2c5fc2e Mon Sep 17 00:00:00 2001 From: Wenson Hsieh Date: Sun, 23 Feb 2025 19:23:59 -0800 Subject: [PATCH 106/174] [macOS] "Update AutoFill Information" prompt incorrectly shows up after performing AutoFill and then closing a tab https://bugs.webkit.org/show_bug.cgi?id=288315 rdar://145085437 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed by Abrar Rahman Protyasha. To improve web compatibility, Safari's AutoFill JavaScript creates and dispatches `TextEvent` on text fields in order to simulate text insertion. This internally triggers typing commands, which marks the text field as having been last modified via user edit. This, in turn, causes Safari to (incorrectly) believe that the user had manually modified the AutoFilled text field after invoking AutoFill, which then shows a prompt. To fix this, we add some plumbing from `TextEvent` → `TypingCommand` → `Editor::appliedEditing` to avoid marking the text field as having been modified through user editing, only in the case where these text editing commands were triggered by a `TextEvent` that was created and initialized from JS bindings. * LayoutTests/fast/forms/textfield-lastchange-was-useredit-expected.txt: * LayoutTests/fast/forms/textfield-lastchange-was-useredit.html: Augment an existing layout test to cover this scenario. * Source/WebCore/dom/TextEvent.cpp: (WebCore::TextEvent::TextEvent): * Source/WebCore/dom/TextEvent.h: Add a flag to mark a `TextEvent`, as being created from JS bindings (as opposed to created from the engine as a result of handling key events, etc.). * Source/WebCore/editing/Editor.cpp: (WebCore::notifyTextFromControls): (WebCore::Editor::appliedEditing): If the edit command is a typing command that was triggered by an event created from bindings, pass `wasUserEdit := false`. (WebCore::Editor::insertTextWithoutSendingTextEvent): (WebCore::Editor::setComposition): * Source/WebCore/editing/EditorCommand.cpp: (WebCore::executeInsertText): * Source/WebCore/editing/TypingCommand.cpp: (WebCore::TypingCommand::insertText): Add plumbing to send `TextEvent*` through `TypingCommand::insertText`, so that we can mark the `TypingCommand` as being triggered by a text event that was created from bindings. * Source/WebCore/editing/TypingCommand.h: Add a flag to mark `TypingCommand` as being triggered by an event that was created from bindings. (isType): Make it possible to use `dynamicDowncast(…)` on an edit command. * Source/WebCore/html/HTMLTextFormControlElement.cpp: (WebCore::HTMLTextFormControlElement::didEditInnerTextValue): * Source/WebCore/html/HTMLTextFormControlElement.h: Plumb `wasUserEdit` down into the text form control, and set `m_lastChangeWasUserEdit`. * Source/WebCore/page/ContextMenuController.cpp: (WebCore::insertUnicodeCharacter): Canonical link: https://commits.webkit.org/290927@main --- ...field-lastchange-was-useredit-expected.txt | 2 ++ .../textfield-lastchange-was-useredit.html | 19 +++++++++++++----- Source/WebCore/dom/TextEvent.cpp | 1 + Source/WebCore/dom/TextEvent.h | 2 ++ Source/WebCore/editing/Editor.cpp | 19 +++++++++++------- Source/WebCore/editing/EditorCommand.cpp | 4 ++-- Source/WebCore/editing/TypingCommand.cpp | 20 +++++++++++++------ Source/WebCore/editing/TypingCommand.h | 15 ++++++++++---- .../html/HTMLTextFormControlElement.cpp | 4 ++-- .../WebCore/html/HTMLTextFormControlElement.h | 2 +- Source/WebCore/page/ContextMenuController.cpp | 2 +- 11 files changed, 62 insertions(+), 28 deletions(-) diff --git a/LayoutTests/fast/forms/textfield-lastchange-was-useredit-expected.txt b/LayoutTests/fast/forms/textfield-lastchange-was-useredit-expected.txt index 5222728cf4c73..f052b703092b1 100644 --- a/LayoutTests/fast/forms/textfield-lastchange-was-useredit-expected.txt +++ b/LayoutTests/fast/forms/textfield-lastchange-was-useredit-expected.txt @@ -9,6 +9,7 @@ PASS textField.value = "WebKit"; internals.wasLastChangeUserEdit(textField) is f PASS textField.style.display = null; internals.wasLastChangeUserEdit(textField) is false PASS document.execCommand("SelectAll", false, null); internals.wasLastChangeUserEdit(textField) is false PASS document.execCommand("Delete", false, null); internals.wasLastChangeUserEdit(textField) is true +PASS dispatchTextEventsInTextField(textField, "foo"); internals.wasLastChangeUserEdit(textField) is false textarea PASS internals.wasLastChangeUserEdit(textField) is false @@ -22,6 +23,7 @@ PASS document.execCommand("Delete", false, null); internals.wasLastChangeUserEdi PASS textField.textContent = "hello\nworld"; internals.wasLastChangeUserEdit(textField) is false PASS document.execCommand("InsertText", false, "\nWebKit rocks"); internals.wasLastChangeUserEdit(textField) is true PASS textField.innerText = " WebKit "; internals.wasLastChangeUserEdit(textField) is false +PASS dispatchTextEventsInTextField(textField, "foo"); internals.wasLastChangeUserEdit(textField) is false PASS successfullyParsed is true TEST COMPLETE diff --git a/LayoutTests/fast/forms/textfield-lastchange-was-useredit.html b/LayoutTests/fast/forms/textfield-lastchange-was-useredit.html index 1ce08972819fc..2afcd6ce7f2f0 100644 --- a/LayoutTests/fast/forms/textfield-lastchange-was-useredit.html +++ b/LayoutTests/fast/forms/textfield-lastchange-was-useredit.html @@ -14,7 +14,16 @@ testFailed('This test requires access to window.internals'); var textField; -function runTest(element) { + +function dispatchTextEventsInTextField(targetElement, text) { + [...text].map(letter => { + const event = document.createEvent("TextEvent"); + event.initTextEvent("textInput", true, true, null, letter); + targetElement.dispatchEvent(event); + }); +} + +[...document.querySelectorAll('input, textarea')].map(element => { debug((textField ? '\n' : '') + element.localName); textField = element; @@ -46,11 +55,11 @@ shouldBeFalse('textField.innerText = " WebKit "; internals.wasLastChangeUserEdit(textField)'); } - textField.parentNode.removeChild(textField); -} + textField.focus(); + shouldBeFalse('dispatchTextEventsInTextField(textField, "foo"); internals.wasLastChangeUserEdit(textField)'); -runTest(document.getElementsByTagName('input')[0]); -runTest(document.getElementsByTagName('textarea')[0]); + textField.parentNode.removeChild(textField); +}); diff --git a/Source/WebCore/dom/TextEvent.cpp b/Source/WebCore/dom/TextEvent.cpp index e87984d377460..7733ee3512ba3 100644 --- a/Source/WebCore/dom/TextEvent.cpp +++ b/Source/WebCore/dom/TextEvent.cpp @@ -70,6 +70,7 @@ TextEvent::TextEvent() : UIEvent(EventInterfaceType::TextEvent) , m_shouldSmartReplace(false) , m_shouldMatchStyle(false) + , m_createdFromBindings(true) , m_mailBlockquoteHandling(MailBlockquoteHandling::RespectBlockquote) { } diff --git a/Source/WebCore/dom/TextEvent.h b/Source/WebCore/dom/TextEvent.h index da3e203327792..145f22978566b 100644 --- a/Source/WebCore/dom/TextEvent.h +++ b/Source/WebCore/dom/TextEvent.h @@ -64,6 +64,7 @@ namespace WebCore { bool shouldSmartReplace() const { return m_shouldSmartReplace; } bool shouldMatchStyle() const { return m_shouldMatchStyle; } + bool createdFromBindings() const { return m_createdFromBindings; } MailBlockquoteHandling mailBlockquoteHandling() const { return m_mailBlockquoteHandling; } DocumentFragment* pastingFragment() const { return m_pastingFragment.get(); } const Vector& dictationAlternatives() const { return m_dictationAlternatives; } @@ -83,6 +84,7 @@ namespace WebCore { RefPtr m_pastingFragment; bool m_shouldSmartReplace; bool m_shouldMatchStyle; + bool m_createdFromBindings { false }; MailBlockquoteHandling m_mailBlockquoteHandling; Vector m_dictationAlternatives; }; diff --git a/Source/WebCore/editing/Editor.cpp b/Source/WebCore/editing/Editor.cpp index 62eb0ccad4732..d7e9093644277 100644 --- a/Source/WebCore/editing/Editor.cpp +++ b/Source/WebCore/editing/Editor.cpp @@ -1124,14 +1124,14 @@ String Editor::selectionStartCSSPropertyValue(CSSPropertyID propertyID) return selectionStyle->style()->getPropertyValue(propertyID); } -static void notifyTextFromControls(Element* startRoot, Element* endRoot) +static void notifyTextFromControls(Element* startRoot, Element* endRoot, bool wasUserEdit = true) { RefPtr startingTextControl { enclosingTextFormControl(firstPositionInOrBeforeNode(startRoot)) }; RefPtr endingTextControl { enclosingTextFormControl(firstPositionInOrBeforeNode(endRoot)) }; if (startingTextControl) - startingTextControl->didEditInnerTextValue(); + startingTextControl->didEditInnerTextValue(wasUserEdit); if (endingTextControl && startingTextControl != endingTextControl) - endingTextControl->didEditInnerTextValue(); + endingTextControl->didEditInnerTextValue(wasUserEdit); } static inline bool shouldRemoveAutocorrectionIndicator(bool shouldConsiderApplyingAutocorrection, bool autocorrectionWasApplied, bool isAutocompletion) @@ -1246,7 +1246,11 @@ void Editor::appliedEditing(CompositeEditCommand& command) Ref composition = *command.composition(); VisibleSelection newSelection(command.endingSelection()); - notifyTextFromControls(composition->startingRootEditableElement(), composition->endingRootEditableElement()); + bool wasUserEdit = [&command] { + RefPtr typingCommand = dynamicDowncast(command); + return !typingCommand || !typingCommand->triggeringEventWasCreatedFromBindings(); + }(); + notifyTextFromControls(composition->startingRootEditableElement(), composition->endingRootEditableElement(), wasUserEdit); if (command.isTopLevelCommand()) { // Don't clear the typing style with this selection change. We do those things elsewhere if necessary. @@ -1454,7 +1458,8 @@ bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectIn options.add(TypingCommand::Option::IsAutocompletion); if (shouldRemoveAutocorrectionIndicator(shouldConsiderApplyingAutocorrection, autocorrectionWasApplied, options.contains(TypingCommand::Option::IsAutocompletion))) options.remove(TypingCommand::Option::RetainAutocorrectionIndicator); - TypingCommand::insertText(document.copyRef(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionType::Final : TypingCommand::TextCompositionType::None); + auto compositionType = triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionType::Final : TypingCommand::TextCompositionType::None; + TypingCommand::insertText(document.copyRef(), text, triggeringEvent, selection, options, compositionType); } // Reveal the current selection. Note that focus may have changed after insertion. @@ -2412,6 +2417,7 @@ void Editor::setComposition(const String& text, const VectorstartDelayingAndCoalescingContentChangeNotifications(); #endif + RefPtr event; RefPtr target { document->focusedElement() }; if (target) { // Dispatch an appropriate composition event to the focused node. @@ -2429,7 +2435,6 @@ void Editor::setComposition(const String& text, const Vector event; if (!m_compositionNode) { // We should send a compositionstart event only when the given text is not empty because this // function doesn't create a composition node when the text is empty. @@ -2465,7 +2470,7 @@ void Editor::setComposition(const String& text, const Vectorselection().selection().base().downstream(); diff --git a/Source/WebCore/editing/EditorCommand.cpp b/Source/WebCore/editing/EditorCommand.cpp index 0a26bbfe9acf9..763a5baaa8773 100644 --- a/Source/WebCore/editing/EditorCommand.cpp +++ b/Source/WebCore/editing/EditorCommand.cpp @@ -532,9 +532,9 @@ static bool executeInsertTab(LocalFrame& frame, Event* event, EditorCommandSourc return targetFrame(frame, event)->eventHandler().handleTextInputEvent("\t"_s, event); } -static bool executeInsertText(LocalFrame& frame, Event*, EditorCommandSource, const String& value) +static bool executeInsertText(LocalFrame& frame, Event* event, EditorCommandSource, const String& value) { - TypingCommand::insertText(*frame.document(), value, { }); + TypingCommand::insertText(*frame.document(), value, event, { }); return true; } diff --git a/Source/WebCore/editing/TypingCommand.cpp b/Source/WebCore/editing/TypingCommand.cpp index 6b15f6f042595..acd7b5a0c1b17 100644 --- a/Source/WebCore/editing/TypingCommand.cpp +++ b/Source/WebCore/editing/TypingCommand.cpp @@ -47,6 +47,7 @@ #include "Range.h" #include "RenderElement.h" #include "StaticRange.h" +#include "TextEvent.h" #include "TextIterator.h" #include "VisibleUnits.h" @@ -222,17 +223,17 @@ void TypingCommand::updateSelectionIfDifferentFromCurrentSelection(TypingCommand typingCommand->setEndingSelection(currentSelection); } -void TypingCommand::insertText(Ref&& document, const String& text, OptionSet