blob: 6fadcd35d49854f4230a9c2c5faf68c87fb54735 [file] [log] [blame]
/*
* Copyright (C) 2014-2016 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 "WebGLBlacklist.h"
#if PLATFORM(MAC)
#import "BlacklistUpdater.h"
#import <OpenGL/OpenGL.h>
#import <pal/spi/cf/CFUtilitiesSPI.h>
namespace WebCore {
struct OSBuildInfo {
OSBuildInfo()
: major(0)
, minor(0)
, build(0)
{
}
OSBuildInfo(int major, int minor, int build)
: major(major)
, minor(minor)
, build(build)
{
}
bool operator==(const OSBuildInfo& other) const
{
return major == other.major && minor == other.minor && build == other.build;
}
bool operator>(const OSBuildInfo& other) const
{
return std::tie(major, minor, build) > std::tie(other.major, other.minor, other.build);
}
bool operator<=(const OSBuildInfo& other) const
{
return std::tie(major, minor, build) <= std::tie(other.major, other.minor, other.build);
}
bool operator<(const OSBuildInfo& other) const
{
return std::tie(major, minor, build) < std::tie(other.major, other.minor, other.build);
}
int major; // Represents the 13 in 13C64.
int minor; // Represents the C in 13C64 (as a number where A == 1, i.e. 3).
int build; // Represents the 64 in 13C64.
};
static OSBuildInfo buildInfoFromOSBuildString(NSString *buildString)
{
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^(\\d+)([A-Z])(\\d+)" options:0 error:&error];
NSArray *matches = [regex matchesInString:buildString options:0 range:NSMakeRange(0, [buildString length])];
if (!matches || matches.count != 1) {
#ifndef NDEBUG
NSLog(@"WebGLBlacklist could not parse OSBuild entry: %@", buildString);
#endif
return OSBuildInfo();
}
NSTextCheckingResult *matchResult = [matches objectAtIndex:0];
if (matchResult.numberOfRanges != 4) {
#ifndef NDEBUG
NSLog(@"WebGLBlacklist could not parse OSBuild entry: %@", buildString);
#endif
return OSBuildInfo();
}
int majorVersion = [[buildString substringWithRange:[matchResult rangeAtIndex:1]] intValue];
int minorVersion = [[buildString substringWithRange:[matchResult rangeAtIndex:2]] characterAtIndex:0] - 'A' + 1;
int buildVersion = [[buildString substringWithRange:[matchResult rangeAtIndex:3]] intValue];
return OSBuildInfo(majorVersion, minorVersion, buildVersion);
}
bool WebGLBlacklist::shouldBlockWebGL()
{
BlacklistUpdater::initializeQueue();
__block bool shouldBlock = false;
dispatch_sync(BlacklistUpdater::queue(), ^{
BlacklistUpdater::reloadIfNecessary();
WebGLBlacklist* webGLBlacklist = BlacklistUpdater::webGLBlacklist();
if (webGLBlacklist)
shouldBlock = webGLBlacklist->shouldBlock();
});
return shouldBlock;
}
bool WebGLBlacklist::shouldSuggestBlockingWebGL()
{
BlacklistUpdater::initializeQueue();
__block bool shouldSuggestBlocking = false;
dispatch_sync(BlacklistUpdater::queue(), ^{
BlacklistUpdater::reloadIfNecessary();
WebGLBlacklist* webGLBlacklist = BlacklistUpdater::webGLBlacklist();
if (webGLBlacklist)
shouldSuggestBlocking = webGLBlacklist->shouldSuggestBlocking();
});
return shouldSuggestBlocking;
}
static bool matchesGPU(GLint machineId, GLint rendererMask)
{
// If the last two bytes of the rendererMask are not zero, then we're
// looking for an exact GPU match. Otherwise we're matching against
// a class of GPUs.
if (rendererMask & 0xFF)
return machineId == rendererMask;
return (machineId & kCGLRendererIDMatchingMask) == rendererMask;
}
static GLint gpuMaskFromString(NSString *input)
{
NSScanner* scanner = [NSScanner scannerWithString:input];
unsigned maskValue;
[scanner scanHexInt:&maskValue];
return static_cast<GLint>(maskValue & (kCGLRendererIDMatchingMask | 0xFF));
}
static bool matchesBuildInfo(OSBuildInfo machineInfo, OSBuildInfo blockInfo, WebGLBlacklist::BlockComparison comparison)
{
switch (comparison) {
case WebGLBlacklist::BlockComparison::Equals:
return machineInfo == blockInfo;
case WebGLBlacklist::BlockComparison::LessThan:
return machineInfo < blockInfo;
case WebGLBlacklist::BlockComparison::LessThanEquals:
return machineInfo <= blockInfo;
}
}
std::unique_ptr<WebGLBlacklist> WebGLBlacklist::create(NSDictionary *propertyList)
{
CFDictionaryRef systemVersionDictionary = _CFCopySystemVersionDictionary();
CFStringRef osBuild = static_cast<CFStringRef>(CFDictionaryGetValue(systemVersionDictionary, _kCFSystemVersionBuildVersionKey));
OSBuildInfo buildInfo = buildInfoFromOSBuildString((__bridge NSString *)osBuild);
CFRelease(systemVersionDictionary);
if (!buildInfo.major)
return nullptr;
NSArray *blockEntries = [propertyList objectForKey:@"WebGLBlacklist"];
if (![blockEntries isKindOfClass:[NSArray class]] || !blockEntries.count)
return nullptr;
CGLPixelFormatAttribute attribs[12] = {
kCGLPFAColorSize, (CGLPixelFormatAttribute)32,
kCGLPFADepthSize, (CGLPixelFormatAttribute)32,
kCGLPFAAccelerated,
kCGLPFASupersample,
kCGLPFAMultisample,
kCGLPFASampleBuffers, (CGLPixelFormatAttribute)1,
kCGLPFASamples, (CGLPixelFormatAttribute)4,
(CGLPixelFormatAttribute)0
};
CGLPixelFormatObj pix;
GLint npix;
CGLChoosePixelFormat(attribs, &pix, &npix);
CGLContextObj ctx;
CGLCreateContext(pix, 0, &ctx);
GLint rendererId = 0;
CGLGetParameter(ctx, kCGLCPCurrentRendererID, &rendererId);
GLint supportsSeparateAddressSpace = 0;
CGLGetParameter(ctx, kCGLCPSupportSeparateAddressSpace, &supportsSeparateAddressSpace);
CGLDestroyContext(ctx);
CGLReleasePixelFormat(pix);
rendererId &= kCGLRendererIDMatchingMask | 0xFF;
BlockCommand globalCommand = BlockCommand::Allow;
for (NSDictionary *blockData in blockEntries) {
GLint gpuMask = gpuMaskFromString([blockData objectForKey:@"GPU"]);
OSBuildInfo blockedBuildInfo = buildInfoFromOSBuildString(static_cast<NSString*>([blockData objectForKey:@"OSBuild"]));
NSString *comparisonString = [blockData objectForKey:@"Comparison"];
BlockComparison comparison = BlockComparison::Equals;
if ([comparisonString isEqualToString:@"LessThan"])
comparison = BlockComparison::LessThan;
else if ([comparisonString isEqualToString:@"LessThanEquals"])
comparison = BlockComparison::LessThanEquals;
NSString *commandString = [blockData objectForKey:@"Command"];
BlockCommand command = BlockCommand::Allow;
if ([commandString isEqualToString:@"Block"])
command = BlockCommand::Block;
else if ([commandString isEqualToString:@"SuggestBlocking"])
command = BlockCommand::SuggestBlocking;
if (matchesGPU(rendererId, gpuMask) && matchesBuildInfo(buildInfo, blockedBuildInfo, comparison)) {
globalCommand = command;
break;
}
}
if (!supportsSeparateAddressSpace && globalCommand == BlockCommand::Allow)
globalCommand = BlockCommand::SuggestBlocking;
return std::unique_ptr<WebGLBlacklist>(new WebGLBlacklist(globalCommand));
}
bool WebGLBlacklist::shouldBlock() const
{
return m_command == BlockCommand::Block;
}
bool WebGLBlacklist::shouldSuggestBlocking() const
{
return m_command == BlockCommand::SuggestBlocking;
}
WebGLBlacklist::WebGLBlacklist(BlockCommand command)
: m_command(command)
{
}
WebGLBlacklist::~WebGLBlacklist()
{
}
}
#endif