[PAL] Move spi/cf directory into PAL
[WebKit.git] / Source / WebCore / platform / mac / WebGLBlacklist.mm
1 /*
2  * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WebGLBlacklist.h"
28
29 #if PLATFORM(MAC)
30
31 #import "BlacklistUpdater.h"
32 #import <OpenGL/OpenGL.h>
33 #import <pal/spi/cf/CFUtilitiesSPI.h>
34
35 namespace WebCore {
36
37 struct OSBuildInfo {
38     OSBuildInfo()
39         : major(0)
40         , minor(0)
41         , build(0)
42     {
43     }
44
45     OSBuildInfo(int major, int minor, int build)
46         : major(major)
47         , minor(minor)
48         , build(build)
49     {
50     }
51
52     bool operator==(const OSBuildInfo& other) const
53     {
54         return major == other.major && minor == other.minor && build == other.build;
55     }
56
57     bool operator>(const OSBuildInfo& other) const
58     {
59         return std::tie(major, minor, build) > std::tie(other.major, other.minor, other.build);
60     }
61
62     bool operator<=(const OSBuildInfo& other) const
63     {
64         return std::tie(major, minor, build) <= std::tie(other.major, other.minor, other.build);
65     }
66
67     bool operator<(const OSBuildInfo& other) const
68     {
69         return std::tie(major, minor, build) < std::tie(other.major, other.minor, other.build);
70     }
71
72     int major; // Represents the 13 in 13C64.
73     int minor; // Represents the C in 13C64 (as a number where A == 1, i.e. 3).
74     int build; // Represents the 64 in 13C64.
75 };
76
77 static OSBuildInfo buildInfoFromOSBuildString(NSString *buildString)
78 {
79     NSError *error = NULL;
80     NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^(\\d+)([A-Z])(\\d+)" options:0 error:&error];
81     NSArray *matches = [regex matchesInString:buildString options:0 range:NSMakeRange(0, [buildString length])];
82     if (!matches || matches.count != 1) {
83 #ifndef NDEBUG
84         NSLog(@"WebGLBlacklist could not parse OSBuild entry: %@", buildString);
85 #endif
86         return OSBuildInfo();
87     }
88
89     NSTextCheckingResult *matchResult = [matches objectAtIndex:0];
90
91     if (matchResult.numberOfRanges != 4) {
92 #ifndef NDEBUG
93         NSLog(@"WebGLBlacklist could not parse OSBuild entry: %@", buildString);
94 #endif
95         return OSBuildInfo();
96     }
97
98     int majorVersion = [[buildString substringWithRange:[matchResult rangeAtIndex:1]] intValue];
99     int minorVersion = [[buildString substringWithRange:[matchResult rangeAtIndex:2]] characterAtIndex:0] - 'A' + 1;
100     int buildVersion = [[buildString substringWithRange:[matchResult rangeAtIndex:3]] intValue];
101
102     return OSBuildInfo(majorVersion, minorVersion, buildVersion);
103 }
104
105 bool WebGLBlacklist::shouldBlockWebGL()
106 {
107     BlacklistUpdater::initializeQueue();
108
109     __block bool shouldBlock = false;
110     dispatch_sync(BlacklistUpdater::queue(), ^{
111         BlacklistUpdater::reloadIfNecessary();
112
113         WebGLBlacklist* webGLBlacklist = BlacklistUpdater::webGLBlacklist();
114         if (webGLBlacklist)
115             shouldBlock = webGLBlacklist->shouldBlock();
116     });
117
118     return shouldBlock;
119 }
120
121 bool WebGLBlacklist::shouldSuggestBlockingWebGL()
122 {
123     BlacklistUpdater::initializeQueue();
124
125     __block bool shouldSuggestBlocking = false;
126     dispatch_sync(BlacklistUpdater::queue(), ^{
127         BlacklistUpdater::reloadIfNecessary();
128
129         WebGLBlacklist* webGLBlacklist = BlacklistUpdater::webGLBlacklist();
130         if (webGLBlacklist)
131             shouldSuggestBlocking = webGLBlacklist->shouldSuggestBlocking();
132     });
133
134     return shouldSuggestBlocking;
135 }
136
137 static bool matchesGPU(GLint machineId, GLint rendererMask)
138 {
139     // If the last two bytes of the rendererMask are not zero, then we're
140     // looking for an exact GPU match. Otherwise we're matching against
141     // a class of GPUs.
142
143     if (rendererMask & 0xFF)
144         return machineId == rendererMask;
145
146     return (machineId & kCGLRendererIDMatchingMask) == rendererMask;
147 }
148
149 static GLint gpuMaskFromString(NSString *input)
150 {
151     NSScanner* scanner = [NSScanner scannerWithString:input];
152     unsigned maskValue;
153     [scanner scanHexInt:&maskValue];
154     return static_cast<GLint>(maskValue & (kCGLRendererIDMatchingMask | 0xFF));
155 }
156
157 static bool matchesBuildInfo(OSBuildInfo machineInfo, OSBuildInfo blockInfo, WebGLBlacklist::BlockComparison comparison)
158 {
159     switch (comparison) {
160     case WebGLBlacklist::BlockComparison::Equals:
161         return machineInfo == blockInfo;
162     case WebGLBlacklist::BlockComparison::LessThan:
163         return machineInfo < blockInfo;
164     case WebGLBlacklist::BlockComparison::LessThanEquals:
165         return machineInfo <= blockInfo;
166     }
167 }
168
169 std::unique_ptr<WebGLBlacklist> WebGLBlacklist::create(NSDictionary *propertyList)
170 {
171     CFDictionaryRef systemVersionDictionary = _CFCopySystemVersionDictionary();
172     CFStringRef osBuild = static_cast<CFStringRef>(CFDictionaryGetValue(systemVersionDictionary, _kCFSystemVersionBuildVersionKey));
173     OSBuildInfo buildInfo = buildInfoFromOSBuildString((NSString *)osBuild);
174     CFRelease(systemVersionDictionary);
175
176     if (!buildInfo.major)
177         return nullptr;
178
179     NSArray *blockEntries = [propertyList objectForKey:@"WebGLBlacklist"];
180
181     if (![blockEntries isKindOfClass:[NSArray class]] || !blockEntries.count)
182         return nullptr;
183
184     CGLPixelFormatAttribute attribs[12] = {
185         kCGLPFAColorSize, (CGLPixelFormatAttribute)32,
186         kCGLPFADepthSize, (CGLPixelFormatAttribute)32,
187         kCGLPFAAccelerated,
188         kCGLPFASupersample,
189         kCGLPFAMultisample,
190         kCGLPFASampleBuffers, (CGLPixelFormatAttribute)1,
191         kCGLPFASamples, (CGLPixelFormatAttribute)4,
192         (CGLPixelFormatAttribute)0
193     };
194
195     CGLPixelFormatObj pix;
196     GLint npix;
197     CGLChoosePixelFormat(attribs, &pix, &npix);
198     CGLContextObj ctx;
199     CGLCreateContext(pix, 0, &ctx);
200     GLint rendererId = 0;
201     CGLGetParameter(ctx, kCGLCPCurrentRendererID, &rendererId);
202     GLint supportsSeparateAddressSpace = 0;
203     CGLGetParameter(ctx, kCGLCPSupportSeparateAddressSpace, &supportsSeparateAddressSpace);
204     CGLDestroyContext(ctx);
205     CGLReleasePixelFormat(pix);
206
207     rendererId &= kCGLRendererIDMatchingMask | 0xFF;
208
209     BlockCommand globalCommand = BlockCommand::Allow;
210
211     for (NSDictionary *blockData in blockEntries) {
212
213         GLint gpuMask = gpuMaskFromString([blockData objectForKey:@"GPU"]);
214
215         OSBuildInfo blockedBuildInfo = buildInfoFromOSBuildString(static_cast<NSString*>([blockData objectForKey:@"OSBuild"]));
216
217         NSString *comparisonString = [blockData objectForKey:@"Comparison"];
218         BlockComparison comparison = BlockComparison::Equals;
219         if ([comparisonString isEqualToString:@"LessThan"])
220             comparison = BlockComparison::LessThan;
221         else if ([comparisonString isEqualToString:@"LessThanEquals"])
222             comparison = BlockComparison::LessThanEquals;
223
224         NSString *commandString = [blockData objectForKey:@"Command"];
225         BlockCommand command = BlockCommand::Allow;
226         if ([commandString isEqualToString:@"Block"])
227             command = BlockCommand::Block;
228         else if ([commandString isEqualToString:@"SuggestBlocking"])
229             command = BlockCommand::SuggestBlocking;
230
231         if (matchesGPU(rendererId, gpuMask) && matchesBuildInfo(buildInfo, blockedBuildInfo, comparison)) {
232             globalCommand = command;
233             break;
234         }
235     }
236
237     if (!supportsSeparateAddressSpace && globalCommand == BlockCommand::Allow)
238         globalCommand = BlockCommand::SuggestBlocking;
239
240     return std::unique_ptr<WebGLBlacklist>(new WebGLBlacklist(globalCommand));
241 }
242
243 bool WebGLBlacklist::shouldBlock() const
244 {
245     return m_command == BlockCommand::Block;
246 }
247
248 bool WebGLBlacklist::shouldSuggestBlocking() const
249 {
250     return m_command == BlockCommand::SuggestBlocking;
251 }
252
253 WebGLBlacklist::WebGLBlacklist(BlockCommand command)
254     : m_command(command)
255 {
256 }
257
258 WebGLBlacklist::~WebGLBlacklist()
259 {
260 }
261
262 }
263 #endif