blob: 54267221bf8175cad48293dd2ab520800ded108d [file] [edit]
/*
* Copyright (C) 2026 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.
*/
#import "config.h"
#import "Helpers/cocoa/FindStateSnapshot.h"
#import "Helpers/cocoa/TestWKWebView.h"
#import <WebKit/WKWebViewPrivate.h>
#import <WebKit/_WKFrameTreeNode.h>
namespace TestWebKitAPI {
static void captureFrameFindStates(TestWKWebView *webView, _WKFrameTreeNode *node, Vector<unsigned>& path, Vector<FrameFindState>& frames)
{
NSDictionary *state = [webView objectByEvaluatingJavaScript:@"({ selectedText: getSelection().toString(), scrollX, scrollY, hasFocus: document.hasFocus() })" inFrame:node.info];
frames.append({
.path = path,
.selectedText = state[@"selectedText"],
.scrollPosition = CGPointMake([state[@"scrollX"] doubleValue], [state[@"scrollY"] doubleValue]),
.hasFocus = static_cast<bool>([state[@"hasFocus"] boolValue]),
});
unsigned index = 0;
for (_WKFrameTreeNode *child in node.childFrames) {
path.append(index++);
captureFrameFindStates(webView, child, path, frames);
path.removeLast();
}
}
static void findTextIndicatorRect(CALayer *layer, CALayer *rootLayer, Class indicatorClass, CGRect& result)
{
if ([layer isKindOfClass:indicatorClass])
result = [layer convertRect:layer.bounds toLayer:rootLayer];
for (CALayer *sublayer in layer.sublayers)
findTextIndicatorRect(sublayer, rootLayer, indicatorClass, result);
}
static CGRect installedTextIndicatorRect(TestWKWebView *webView)
{
CGRect result = CGRectNull;
findTextIndicatorRect(webView.layer, webView.layer, NSClassFromString(@"WebTextIndicatorLayer"), result);
return result;
}
FindStateSnapshot FindStateSnapshot::capture(TestWKWebView *webView)
{
FindStateSnapshot snapshot;
Vector<unsigned> path;
captureFrameFindStates(webView, [webView mainFrame], path, snapshot.frames);
snapshot.textIndicatorRect = installedTextIndicatorRect(webView);
#if HAVE(UIFINDINTERACTION)
snapshot.hasFindOverlayLayer = static_cast<bool>(webView._layerForFindOverlay);
#endif
#if PLATFORM(IOS_FAMILY)
snapshot.contentOffset = webView.scrollView.contentOffset;
#endif
return snapshot;
}
std::optional<FrameFindState> FindStateSnapshot::frame(const Vector<unsigned>& path) const
{
auto it = std::ranges::find(frames, path, &FrameFindState::path);
if (it == frames.end())
return std::nullopt;
return *it;
}
Vector<Vector<unsigned>> FindStateSnapshot::framesWithSelection() const
{
Vector<Vector<unsigned>> result;
for (auto& frame : frames) {
if (!frame.selectedText.isEmpty())
result.append(frame.path);
}
return result;
}
} // namespace TestWebKitAPI