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