LayoutTestHelper should set the color profile of all displays
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Feb 2015 22:38:46 +0000 (22:38 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Feb 2015 22:38:46 +0000 (22:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=141260

Reviewed by Tim Horton.

WebKitTestRunner can (erroneously) grab the colorspace of the "main" screen.
which is the screen with the active window. Make things more robust by changing
the colorspace of all displays, not just the main screen, when running layout tests.

* DumpRenderTree/mac/Configurations/LayoutTestHelper.xcconfig: Enable ARC
* DumpRenderTree/mac/LayoutTestHelper.m:
(originalColorProfileURLs):
(colorProfileURLForDisplay):
(displayUUIDStrings):
(saveDisplayColorProfiles):
(setDisplayColorProfile):
(restoreDisplayColorProfiles):
(installLayoutTestColorProfile):
(restoreUserColorProfile):
(main):
Store display color profiles by map of UUID strings to URLs (NSUUID and CFUUID are not
toll-free bridged, sadly). Use the map to restore all profiles on exit.
Convert to use more Obj-C types.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@179637 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/ChangeLog
Tools/DumpRenderTree/mac/LayoutTestHelper.m

index 48484d3..88ab51e 100644 (file)
@@ -1,3 +1,29 @@
+2015-02-04  Simon Fraser  <simon.fraser@apple.com>
+
+        LayoutTestHelper should set the color profile of all displays
+        https://bugs.webkit.org/show_bug.cgi?id=141260
+
+        Reviewed by Tim Horton.
+        
+        WebKitTestRunner can (erroneously) grab the colorspace of the "main" screen.
+        which is the screen with the active window. Make things more robust by changing
+        the colorspace of all displays, not just the main screen, when running layout tests.
+
+        * DumpRenderTree/mac/Configurations/LayoutTestHelper.xcconfig: Enable ARC
+        * DumpRenderTree/mac/LayoutTestHelper.m: 
+        (originalColorProfileURLs):
+        (colorProfileURLForDisplay):
+        (displayUUIDStrings):
+        (saveDisplayColorProfiles):
+        (setDisplayColorProfile):
+        (restoreDisplayColorProfiles):
+        (installLayoutTestColorProfile):
+        (restoreUserColorProfile):
+        (main):        
+        Store display color profiles by map of UUID strings to URLs (NSUUID and CFUUID are not
+        toll-free bridged, sadly). Use the map to restore all profiles on exit.
+        Convert to use more Obj-C types.
+
 2015-02-04  Daniel Bates  <dabates@apple.com>
 
         test-webkitpy fails on Mac without iphoneos SDK
index bc8aec8..6802a20 100644 (file)
 
 static int installColorProfile = false;
 
-static CFURLRef sUserColorProfileURL;
+static NSMutableDictionary *originalColorProfileURLs()
+{
+    static NSMutableDictionary *sharedInstance;
+    if (!sharedInstance)
+        sharedInstance = [[NSMutableDictionary alloc] init];
+    return sharedInstance;
+}
 
-static void installLayoutTestColorProfile()
+static NSURL *colorProfileURLForDisplay(NSString *displayUUIDString)
 {
-    if (!installColorProfile)
-        return;
+    CFUUIDRef uuid = CFUUIDCreateFromString(kCFAllocatorDefault, (CFStringRef)displayUUIDString);
+    CFDictionaryRef deviceInfo = ColorSyncDeviceCopyDeviceInfo(kColorSyncDisplayDeviceClass, uuid);
+    CFRelease(uuid);
+    if (!deviceInfo) {
+        NSLog(@"No display attached to system; not setting main display's color profile.");
+        return nil;
+    }
 
-    // To make sure we get consistent colors (not dependent on the chosen color
-    // space of the main display), we force the generic RGB color profile.
-    // This causes a change the user can see.
+    CFURLRef profileURL;
+    CFDictionaryRef profileInfo = (CFDictionaryRef)CFDictionaryGetValue(deviceInfo, kColorSyncCustomProfiles);
+    if (profileInfo)
+        profileURL = (CFURLRef)CFDictionaryGetValue(profileInfo, CFSTR("1"));
+    else {
+        profileInfo = (CFDictionaryRef)CFDictionaryGetValue(deviceInfo, kColorSyncFactoryProfiles);
+        CFDictionaryRef factoryProfile = (CFDictionaryRef)CFDictionaryGetValue(profileInfo, CFSTR("1"));
+        profileURL = (CFURLRef)CFDictionaryGetValue(factoryProfile, kColorSyncDeviceProfileURL);
+    }
     
-    CFUUIDRef mainDisplayID = CGDisplayCreateUUIDFromDisplayID(CGMainDisplayID());
     
-    if (!sUserColorProfileURL) {
-        CFDictionaryRef deviceInfo = ColorSyncDeviceCopyDeviceInfo(kColorSyncDisplayDeviceClass, mainDisplayID);
+    NSURL *url = (NSURL *)CFAutorelease(CFRetain(profileURL));
+    CFRelease(deviceInfo);
+    return url;
+}
 
-        if (!deviceInfo) {
-            NSLog(@"No display attached to system; not setting main display's color profile.");
-            CFRelease(mainDisplayID);
-            return;
-        }
+static NSArray *displayUUIDStrings()
+{
+    NSMutableArray *result = [NSMutableArray array];
 
-        CFDictionaryRef profileInfo = (CFDictionaryRef)CFDictionaryGetValue(deviceInfo, kColorSyncCustomProfiles);
-        if (profileInfo) {
-            sUserColorProfileURL = (CFURLRef)CFDictionaryGetValue(profileInfo, CFSTR("1"));
-            CFRetain(sUserColorProfileURL);
-        } else {
-            profileInfo = (CFDictionaryRef)CFDictionaryGetValue(deviceInfo, kColorSyncFactoryProfiles);
-            CFDictionaryRef factoryProfile = (CFDictionaryRef)CFDictionaryGetValue(profileInfo, CFSTR("1"));
-            sUserColorProfileURL = (CFURLRef)CFDictionaryGetValue(factoryProfile, kColorSyncDeviceProfileURL);
-            CFRetain(sUserColorProfileURL);
-        }
+    static const uint32_t maxDisplayCount = 10;
+    CGDirectDisplayID displayIDs[maxDisplayCount] = { 0 };
+    uint32_t displayCount = 0;
+    
+    CGError err = CGGetActiveDisplayList(maxDisplayCount, displayIDs, &displayCount);
+    if (err != kCGErrorSuccess) {
+        NSLog(@"Error %d getting active display list; not setting display color profile.", err);
+        return nil;
+    }
+
+    if (!displayCount) {
+        NSLog(@"No display attached to system; not setting display color profile.");
+        return nil;
+    }
+
+    for (uint32_t i = 0; i < displayCount; ++i) {
+        CFUUIDRef displayUUIDRef = CGDisplayCreateUUIDFromDisplayID(displayIDs[i]);
+        [result addObject:(NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, displayUUIDRef))];
+        CFRelease(displayUUIDRef);
+    }
+    
+    return result;
+}
+
+static void saveDisplayColorProfiles(NSArray *displayUUIDStrings)
+{
+    NSMutableDictionary *userColorProfiles = originalColorProfileURLs();
+
+    for (NSString *UUIDString in displayUUIDStrings) {
+        if ([userColorProfiles objectForKey:UUIDString])
+            continue;
+        
+        NSURL *colorProfileURL = colorProfileURLForDisplay(UUIDString);
+        [userColorProfiles setObject:colorProfileURL forKey:UUIDString];
+    }
+}
+
+static void setDisplayColorProfile(NSString *displayUUIDString, NSURL *colorProfileURL)
+{
+    NSDictionary *profileInfo = @{
+        (NSString *)kColorSyncDeviceDefaultProfileID : colorProfileURL
+    };
+
+    CFUUIDRef uuid = CFUUIDCreateFromString(kCFAllocatorDefault, (CFStringRef)displayUUIDString);
+    BOOL success = ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, uuid, (CFDictionaryRef)profileInfo);
+    if (!success)
+        NSLog(@"Failed to set color profile for display %@! Many pixel tests may fail as a result.", displayUUIDString);
+    CFRelease(uuid);
+}
+
+static void restoreDisplayColorProfiles(NSArray *displayUUIDStrings)
+{
+    NSMutableDictionary* userColorProfiles = originalColorProfileURLs();
+
+    for (NSString *UUIDString in displayUUIDStrings) {
+        NSURL *profileURL = [userColorProfiles objectForKey:UUIDString];
+        if (!profileURL)
+            continue;
         
-        CFRelease(deviceInfo);
+        setDisplayColorProfile(UUIDString, profileURL);
     }
+}
+
+static void installLayoutTestColorProfile()
+{
+    if (!installColorProfile)
+        return;
+
+    // To make sure we get consistent colors (not dependent on the chosen color
+    // space of the display), we force the generic sRGB color profile on all displays.
+    // This causes a change the user can see.
+
+    NSArray *displays = displayUUIDStrings();
+    saveDisplayColorProfiles(displays);
 
     ColorSyncProfileRef sRGBProfile = ColorSyncProfileCreateWithName(kColorSyncSRGBProfile);
     CFErrorRef error;
     CFURLRef profileURL = ColorSyncProfileGetURL(sRGBProfile, &error);
     if (!profileURL) {
         NSLog(@"Failed to get URL of Generic RGB color profile! Many pixel tests may fail as a result. Error: %@", error);
-        
-        if (sUserColorProfileURL) {
-            CFRelease(sUserColorProfileURL);
-            sUserColorProfileURL = 0;
-        }
-        
-        CFRelease(sRGBProfile);
-        CFRelease(mainDisplayID);
         return;
     }
-        
-    CFMutableDictionaryRef profileInfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-    CFDictionarySetValue(profileInfo, kColorSyncDeviceDefaultProfileID, profileURL);
-    
-    if (!ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, mainDisplayID, profileInfo)) {
-        NSLog(@"Failed to set color profile for main display! Many pixel tests may fail as a result.");
-        
-        if (sUserColorProfileURL) {
-            CFRelease(sUserColorProfileURL);
-            sUserColorProfileURL = 0;
-        }
-    }
     
-    CFRelease(profileInfo);
-    CFRelease(sRGBProfile);
-    CFRelease(mainDisplayID);
+    for (NSString *displayUUIDString in displays)
+        setDisplayColorProfile(displayUUIDString, (NSURL *)profileURL);
 }
 
 static void restoreUserColorProfile(void)
@@ -122,15 +178,8 @@ static void restoreUserColorProfile(void)
     // This is used as a signal handler, and thus the calls into ColorSync are unsafe.
     // But we might as well try to restore the user's color profile, we're going down anyway...
     
-    if (!sUserColorProfileURL)
-        return;
-    
-    CFUUIDRef mainDisplayID = CGDisplayCreateUUIDFromDisplayID(CGMainDisplayID());
-    CFMutableDictionaryRef profileInfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-    CFDictionarySetValue(profileInfo, kColorSyncDeviceDefaultProfileID, sUserColorProfileURL);
-    ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, mainDisplayID, profileInfo);
-    CFRelease(mainDisplayID);
-    CFRelease(profileInfo);
+    NSArray *displays = displayUUIDStrings();
+    restoreDisplayColorProfiles(displays);
 }
 
 static void simpleSignalHandler(int sig)
@@ -188,8 +237,6 @@ int main(int argc, char* argv[])
         }
     }
 
-    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-
     // Hooks the ways we might get told to clean up...
     signal(SIGINT, simpleSignalHandler);
     signal(SIGHUP, simpleSignalHandler);
@@ -210,7 +257,6 @@ int main(int argc, char* argv[])
     // Restore the profile
     restoreUserColorProfile();
 
-    [pool release];
     return 0;
 }