blob: dacb85c2e076d8be4582ec59173afffd638ceb64 [file] [log] [blame]
/*
* Copyright (C) 2021 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 "TestCocoa.h"
#import "TestWKWebView.h"
#import <WebCore/ColorCocoa.h>
#import <WebKit/WKWebViewPrivate.h>
#import <wtf/RetainPtr.h>
#define EXPECT_IN_RANGE(actual, min, max) \
EXPECT_GE(actual, min); \
EXPECT_LE(actual, max);
#define EXPECT_NSSTRING_EQ(expected, actual) \
EXPECT_TRUE([actual isKindOfClass:[NSString class]]); \
EXPECT_WK_STREQ(expected, (NSString *)actual);
static RetainPtr<CGColor> defaultBackgroundColor()
{
#if HAVE(OS_DARK_MODE_SUPPORT) && PLATFORM(MAC)
auto color = retainPtr(NSColor.controlBackgroundColor);
#elif HAVE(OS_DARK_MODE_SUPPORT) && PLATFORM(IOS_FAMILY)
auto color = retainPtr(UIColor.systemBackgroundColor);
#else
auto color = retainPtr([WebCore::CocoaColor whiteColor]);
#endif
// Some of the above can sometimes be a monochrome color, so convert it to sRGB so the comparisons below work.
// `WebCore::ColorSpace` doesn't have an equivalent monochrome enum value, but treats `CGColor` with only two components as monochrome and converts them to `SRGB`.
auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
auto sRGBColor = adoptCF(CGColorCreateCopyByMatchingToColorSpace(sRGBColorSpace.get(), kCGRenderingIntentDefault, [color CGColor], NULL));
return sRGBColor.get();
}
TEST(WKWebViewUnderPageBackgroundColor, OnLoad)
{
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
}
TEST(WKWebViewUnderPageBackgroundColor, SingleSolidColor)
{
auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
[webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<style> body { background-color: red; } </style>"];
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
}
TEST(WKWebViewUnderPageBackgroundColor, SingleBlendedColor)
{
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
[webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<style> body { background-color: rgba(255, 0, 0, 0.5); } </style>"];
auto components = CGColorGetComponents([webView underPageBackgroundColor].CGColor);
EXPECT_EQ(components[0], 1);
EXPECT_IN_RANGE(components[1], 0.45, 0.55);
EXPECT_IN_RANGE(components[2], 0.45, 0.55);
EXPECT_EQ(components[3], 1);
}
TEST(WKWebViewUnderPageBackgroundColor, MultipleSolidColors)
{
auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
[webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<style> html { background-color: blue; } body { background-color: red; } </style>"];
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
}
TEST(WKWebViewUnderPageBackgroundColor, MultipleBlendedColors)
{
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
[webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<style> html { background-color: rgba(255, 0, 0, 0.5); } body { background-color: rgba(0, 0, 255, 0.5); } </style>"];
auto components = CGColorGetComponents([webView underPageBackgroundColor].CGColor);
EXPECT_IN_RANGE(components[0], 0.45, 0.55);
EXPECT_IN_RANGE(components[1], 0.2, 0.25);
EXPECT_IN_RANGE(components[2], 0.7, 0.75);
EXPECT_EQ(components[3], 1);
}
@interface WKWebViewUnderPageBackgroundColorObserver : NSObject
- (instancetype)initWithWebView:(WKWebView *)webView;
@property (nonatomic, readonly) WKWebView *webView;
@property (nonatomic, copy) NSString *state;
@end
@implementation WKWebViewUnderPageBackgroundColorObserver
- (instancetype)initWithWebView:(WKWebView *)webView
{
if (!(self = [super init]))
return nil;
_state = @"before-init";
_webView = webView;
[_webView addObserver:self forKeyPath:@"underPageBackgroundColor" options:NSKeyValueObservingOptionInitial context:nil];
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([_state isEqualToString:@"before-init"]) {
_state = @"after-init";
return;
}
if ([_state isEqualToString:@"before-css-class-added"]) {
_state = @"after-css-class-added";
return;
}
if ([_state isEqualToString:@"before-css-class-changed"]) {
_state = @"after-css-class-changed";
return;
}
if ([_state isEqualToString:@"before-css-class-removed"]) {
_state = @"after-css-class-removed";
return;
}
if ([_state isEqualToString:@"before-nonnull-override"]) {
_state = @"after-nonnull-override";
return;
}
if ([_state isEqualToString:@"before-null-override"]) {
_state = @"after-null-override";
return;
}
EXPECT_NSSTRING_EQ("not-reached", _state);
}
@end
TEST(WKWebViewUnderPageBackgroundColor, KVO)
{
auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
auto blueColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), blueColorComponents));
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
auto underPageBackgroundColorObserver = adoptNS([[WKWebViewUnderPageBackgroundColorObserver alloc] initWithWebView:webView.get()]);
EXPECT_NSSTRING_EQ("after-init", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<style> body.red { background-color: red; } body.blue { background-color: blue; } </style>"];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
[underPageBackgroundColorObserver setState:@"before-css-class-added"];
[webView objectByEvaluatingJavaScript:@"document.body.className = 'red'"];
[webView waitForNextPresentationUpdate];
EXPECT_NSSTRING_EQ("after-css-class-added", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView setUnderPageBackgroundColor:[WebCore::CocoaColor colorWithCGColor:redColor.get()]];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView setUnderPageBackgroundColor:nil];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
[underPageBackgroundColorObserver setState:@"before-css-class-changed"];
[webView objectByEvaluatingJavaScript:@"document.body.className = 'blue'"];
[webView waitForNextPresentationUpdate];
EXPECT_NSSTRING_EQ("after-css-class-changed", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, blueColor.get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView setUnderPageBackgroundColor:[WebCore::CocoaColor colorWithCGColor:blueColor.get()]];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, blueColor.get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView setUnderPageBackgroundColor:nil];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, blueColor.get()));
[underPageBackgroundColorObserver setState:@"before-css-class-removed"];
[webView objectByEvaluatingJavaScript:@"document.body.className = ''"];
[webView waitForNextPresentationUpdate];
EXPECT_NSSTRING_EQ("after-css-class-removed", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView setUnderPageBackgroundColor:[WebCore::CocoaColor colorWithCGColor:defaultBackgroundColor().get()]];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView setUnderPageBackgroundColor:nil];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
[underPageBackgroundColorObserver setState:@"before-nonnull-override"];
[webView setUnderPageBackgroundColor:[WebCore::CocoaColor colorWithCGColor:redColor.get()]];
EXPECT_NSSTRING_EQ("after-nonnull-override", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView setUnderPageBackgroundColor:[WebCore::CocoaColor colorWithCGColor:redColor.get()]];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView objectByEvaluatingJavaScript:@"document.body.className = 'red'"];
[webView waitForNextPresentationUpdate];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView objectByEvaluatingJavaScript:@"document.body.className = 'blue'"];
[webView waitForNextPresentationUpdate];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
[underPageBackgroundColorObserver setState:@"should-not-change"];
[webView objectByEvaluatingJavaScript:@"document.body.className = ''"];
[webView waitForNextPresentationUpdate];
EXPECT_NSSTRING_EQ("should-not-change", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
[underPageBackgroundColorObserver setState:@"before-null-override"];
[webView setUnderPageBackgroundColor:nil];
EXPECT_NSSTRING_EQ("after-null-override", [underPageBackgroundColorObserver state]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
}
#if PLATFORM(IOS_FAMILY)
constexpr CGFloat whiteColorComponents[4] = { 1, 1, 1, 1 };
// There's no API/SPI to get the background color of the scroll area on macOS.
TEST(WKWebViewUnderPageBackgroundColor, MatchesScrollView)
{
auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
auto blueColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), blueColorComponents));
auto whiteColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), whiteColorComponents));
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, defaultBackgroundColor().get()));
EXPECT_TRUE(CGColorEqualToColor([webView scrollView].backgroundColor.CGColor, defaultBackgroundColor().get()));
[webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<style> body { background-color: red; } </style>"];
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, redColor.get()));
EXPECT_TRUE(CGColorEqualToColor([webView scrollView].backgroundColor.CGColor, redColor.get()));
EXPECT_EQ([webView scrollView].indicatorStyle, UIScrollViewIndicatorStyleWhite);
[webView setUnderPageBackgroundColor:[WebCore::CocoaColor colorWithCGColor:blueColor.get()]];
[webView waitForNextPresentationUpdate];
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, blueColor.get()));
EXPECT_TRUE(CGColorEqualToColor([webView scrollView].backgroundColor.CGColor, blueColor.get()));
EXPECT_EQ([webView scrollView].indicatorStyle, UIScrollViewIndicatorStyleWhite);
[webView setUnderPageBackgroundColor:[WebCore::CocoaColor colorWithCGColor:whiteColor.get()]];
[webView waitForNextPresentationUpdate];
EXPECT_TRUE(CGColorEqualToColor([webView underPageBackgroundColor].CGColor, whiteColor.get()));
EXPECT_TRUE(CGColorEqualToColor([webView scrollView].backgroundColor.CGColor, whiteColor.get()));
EXPECT_EQ([webView scrollView].indicatorStyle, UIScrollViewIndicatorStyleWhite);
}
#endif // PLATFORM(IOS_FAMILY)