/* * Copyright (C) 2010 Google Inc. All rights reserved. * Copyright (C) 2012 Apple Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT * OWNER OR 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" #if !PLATFORM(IOS_FAMILY) #import #import #import #import #import #import #import #if USE(APPLE_INTERNAL_SDK) #import #else CFUUIDRef CGDisplayCreateUUIDFromDisplayID(uint32_t displayID); #endif // This is a simple helper app that changes the color profile of the main display // to GenericRGB and back when done. This program is managed by the layout // test script, so it can do the job for multiple DumpRenderTree while they are // running layout tests. static int installColorProfile = false; static int preferIntegratedGPU = false; static uint32_t assertionIDForDisplaySleep = 0; static uint32_t assertionIDForSystemSleep = 0; static NSMutableDictionary *originalColorProfileURLs() { static NSMutableDictionary *sharedInstance; if (!sharedInstance) sharedInstance = [[NSMutableDictionary alloc] init]; return sharedInstance; } static NSURL *colorProfileURLForDisplay(NSString *displayUUIDString) { CFUUIDRef uuid = CFUUIDCreateFromString(kCFAllocatorDefault, (CFStringRef)displayUUIDString); CFDictionaryRef deviceInfo = ColorSyncDeviceCopyDeviceInfo(kColorSyncDisplayDeviceClass, uuid); CFRelease(uuid); if (!deviceInfo) { NSLog(@"Could not retrieve device info from ColorSync; not setting main display's color profile."); return nil; } CFStringRef profileID = CFSTR("1"); CFURLRef profileURL = nil; CFDictionaryRef factoryProfiles = (CFDictionaryRef)CFDictionaryGetValue(deviceInfo, kColorSyncFactoryProfiles); if (factoryProfiles) profileID = (CFStringRef)CFDictionaryGetValue(factoryProfiles, kColorSyncDeviceDefaultProfileID); CFDictionaryRef customProfiles = (CFDictionaryRef)CFDictionaryGetValue(deviceInfo, kColorSyncCustomProfiles); if (customProfiles) profileURL = (CFURLRef)CFDictionaryGetValue(customProfiles, profileID); if (!profileURL && factoryProfiles) { CFDictionaryRef profile = (CFDictionaryRef)CFDictionaryGetValue(factoryProfiles, profileID); if (profile) profileURL = (CFURLRef)CFDictionaryGetValue(profile, kColorSyncDeviceProfileURL); } if (!profileURL) { NSLog(@"Could not determine current color profile, so it will not be reset after running the tests."); CFRelease(deviceInfo); return nil; } NSURL *url = CFBridgingRelease(CFRetain(profileURL)); CFRelease(deviceInfo); return url; } static NSArray *displayUUIDStrings() { NSMutableArray *result = [NSMutableArray array]; enum { maxDisplayCount = 10 }; CGDirectDisplayID displayIDs[maxDisplayCount] = { 0 }; uint32_t displayCount = 0; CGError err = CGGetOnlineDisplayList(maxDisplayCount, displayIDs, &displayCount); if (err != kCGErrorSuccess) { NSLog(@"Error %d getting online 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); if (!colorProfileURL) continue; [userColorProfiles setObject:colorProfileURL forKey:UUIDString]; } } static void setDisplayColorProfile(NSString *displayUUIDString, NSURL *colorProfileURL) { NSDictionary *profileInfo = @{ (__bridge 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; 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); // Profile path needs to be hardcoded because of . NSURL *sRGBProfileURL = [NSURL fileURLWithPath:@"/System/Library/ColorSync/Profiles/sRGB Profile.icc"]; for (NSString *displayUUIDString in displays) setDisplayColorProfile(displayUUIDString, sRGBProfileURL); } static void restoreUserColorProfile(void) { if (!installColorProfile) return; // 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... NSArray *displays = displayUUIDStrings(); restoreDisplayColorProfiles(displays); } static void releaseSleepAssertions() { IOPMAssertionRelease(assertionIDForDisplaySleep); IOPMAssertionRelease(assertionIDForSystemSleep); } static void simpleSignalHandler(int sig) { // Try to restore the color profile and try to go down cleanly restoreUserColorProfile(); releaseSleepAssertions(); exit(128 + sig); } static void lockDownDiscreteGraphics() { mach_port_t masterPort; kern_return_t kernResult = IOMasterPort(bootstrap_port, &masterPort); if (kernResult != KERN_SUCCESS) return; CFDictionaryRef classToMatch = IOServiceMatching("AppleGraphicsControl"); if (!classToMatch) return; io_service_t serviceObject = IOServiceGetMatchingService(masterPort, classToMatch); if (!serviceObject) { // The machine does not allow control over the choice of graphics device. return; } // We're intentionally leaking this io_connect in order for the process to stay locked to discrete graphics // for the lifetime of the service connection. static io_connect_t permanentLockDownService = 0; // This call stalls until the graphics device lock is granted. kernResult = IOServiceOpen(serviceObject, mach_task_self(), 1, &permanentLockDownService); if (kernResult != KERN_SUCCESS) { NSLog(@"IOServiceOpen() failed in %s with kernResult = %d", __FUNCTION__, kernResult); return; } kernResult = IOObjectRelease(serviceObject); if (kernResult != KERN_SUCCESS) NSLog(@"IOObjectRelease() failed in %s with kernResult = %d", __FUNCTION__, kernResult); } static void addSleepAssertions() { CFStringRef assertionName = CFSTR("WebKit LayoutTestHelper"); CFStringRef assertionDetails = CFSTR("WebKit layout-test helper tool is preventing sleep."); IOPMAssertionCreateWithDescription(kIOPMAssertionTypePreventUserIdleDisplaySleep, assertionName, assertionDetails, assertionDetails, NULL, 0, NULL, &assertionIDForDisplaySleep); IOPMAssertionCreateWithDescription(kIOPMAssertionTypePreventUserIdleSystemSleep, assertionName, assertionDetails, assertionDetails, NULL, 0, NULL, &assertionIDForSystemSleep); } int main(int argc, char* argv[]) { struct option options[] = { { "install-color-profile", no_argument, &installColorProfile, true }, { "prefer-integrated-gpu", no_argument, &preferIntegratedGPU, true }, }; int option; while ((option = getopt_long(argc, (char* const*)argv, "", options, NULL)) != -1) { switch (option) { case '?': // unknown or ambiguous option case ':': // missing argument exit(1); break; } } // Hooks the ways we might get told to clean up... signal(SIGINT, simpleSignalHandler); signal(SIGHUP, simpleSignalHandler); signal(SIGTERM, simpleSignalHandler); addSleepAssertions(); if (!preferIntegratedGPU) lockDownDiscreteGraphics(); // Save off the current profile, and then install the layout test profile. installLayoutTestColorProfile(); // Let the script know we're ready printf("ready\n"); fflush(stdout); // Wait for any key (or signal) getchar(); // Restore the profile restoreUserColorProfile(); releaseSleepAssertions(); return 0; } #endif // !PLATFORM(IOS_FAMILY) #if PLATFORM(IOS_FAMILY) int main(int argc, char* argv[]) { return 0; } #endif