| /* |
| * 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 |