michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: michael@0: #ifndef MacQuirks_h__ michael@0: #define MacQuirks_h__ michael@0: michael@0: #include michael@0: #include michael@0: #include "CoreFoundation/CoreFoundation.h" michael@0: #include "CoreServices/CoreServices.h" michael@0: #include "Carbon/Carbon.h" michael@0: michael@0: // This file is a copy and paste from existing methods from michael@0: // libxul. This is intentional because this interpose michael@0: // library does not link with libxul. michael@0: michael@0: struct VersionPart { michael@0: int32_t numA; michael@0: michael@0: const char *strB; // NOT null-terminated, can be a null pointer michael@0: uint32_t strBlen; michael@0: michael@0: int32_t numC; michael@0: michael@0: char *extraD; // null-terminated michael@0: }; michael@0: michael@0: /** michael@0: * Parse a version part into a number and "extra text". michael@0: * michael@0: * @returns A pointer to the next versionpart, or null if none. michael@0: */ michael@0: static char* michael@0: ParseVP(char *part, VersionPart &result) michael@0: { michael@0: char *dot; michael@0: michael@0: result.numA = 0; michael@0: result.strB = nullptr; michael@0: result.strBlen = 0; michael@0: result.numC = 0; michael@0: result.extraD = nullptr; michael@0: michael@0: if (!part) michael@0: return part; michael@0: michael@0: dot = strchr(part, '.'); michael@0: if (dot) michael@0: *dot = '\0'; michael@0: michael@0: if (part[0] == '*' && part[1] == '\0') { michael@0: result.numA = INT32_MAX; michael@0: result.strB = ""; michael@0: } michael@0: else { michael@0: result.numA = strtol(part, const_cast(&result.strB), 10); michael@0: } michael@0: michael@0: if (!*result.strB) { michael@0: result.strB = nullptr; michael@0: result.strBlen = 0; michael@0: } michael@0: else { michael@0: if (result.strB[0] == '+') { michael@0: static const char kPre[] = "pre"; michael@0: michael@0: ++result.numA; michael@0: result.strB = kPre; michael@0: result.strBlen = sizeof(kPre) - 1; michael@0: } michael@0: else { michael@0: const char *numstart = strpbrk(result.strB, "0123456789+-"); michael@0: if (!numstart) { michael@0: result.strBlen = strlen(result.strB); michael@0: } michael@0: else { michael@0: result.strBlen = numstart - result.strB; michael@0: michael@0: result.numC = strtol(numstart, &result.extraD, 10); michael@0: if (!*result.extraD) michael@0: result.extraD = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (dot) { michael@0: ++dot; michael@0: michael@0: if (!*dot) michael@0: dot = nullptr; michael@0: } michael@0: michael@0: return dot; michael@0: } michael@0: michael@0: michael@0: // compare two null-terminated strings, which may be null pointers michael@0: static int32_t michael@0: ns_strcmp(const char *str1, const char *str2) michael@0: { michael@0: // any string is *before* no string michael@0: if (!str1) michael@0: return str2 != 0; michael@0: michael@0: if (!str2) michael@0: return -1; michael@0: michael@0: return strcmp(str1, str2); michael@0: } michael@0: michael@0: // compare two length-specified string, which may be null pointers michael@0: static int32_t michael@0: ns_strnncmp(const char *str1, uint32_t len1, const char *str2, uint32_t len2) michael@0: { michael@0: // any string is *before* no string michael@0: if (!str1) michael@0: return str2 != 0; michael@0: michael@0: if (!str2) michael@0: return -1; michael@0: michael@0: for (; len1 && len2; --len1, --len2, ++str1, ++str2) { michael@0: if (*str1 < *str2) michael@0: return -1; michael@0: michael@0: if (*str1 > *str2) michael@0: return 1; michael@0: } michael@0: michael@0: if (len1 == 0) michael@0: return len2 == 0 ? 0 : -1; michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: // compare two int32_t michael@0: static int32_t michael@0: ns_cmp(int32_t n1, int32_t n2) michael@0: { michael@0: if (n1 < n2) michael@0: return -1; michael@0: michael@0: return n1 != n2; michael@0: } michael@0: michael@0: /** michael@0: * Compares two VersionParts michael@0: */ michael@0: static int32_t michael@0: CompareVP(VersionPart &v1, VersionPart &v2) michael@0: { michael@0: int32_t r = ns_cmp(v1.numA, v2.numA); michael@0: if (r) michael@0: return r; michael@0: michael@0: r = ns_strnncmp(v1.strB, v1.strBlen, v2.strB, v2.strBlen); michael@0: if (r) michael@0: return r; michael@0: michael@0: r = ns_cmp(v1.numC, v2.numC); michael@0: if (r) michael@0: return r; michael@0: michael@0: return ns_strcmp(v1.extraD, v2.extraD); michael@0: } michael@0: michael@0: /* this is intentionally not static so that we don't end up making copies michael@0: * anywhere */ michael@0: int32_t michael@0: NS_CompareVersions(const char *A, const char *B) michael@0: { michael@0: char *A2 = strdup(A); michael@0: if (!A2) michael@0: return 1; michael@0: michael@0: char *B2 = strdup(B); michael@0: if (!B2) { michael@0: free(A2); michael@0: return 1; michael@0: } michael@0: michael@0: int32_t result; michael@0: char *a = A2, *b = B2; michael@0: michael@0: do { michael@0: VersionPart va, vb; michael@0: michael@0: a = ParseVP(a, va); michael@0: b = ParseVP(b, vb); michael@0: michael@0: result = CompareVP(va, vb); michael@0: if (result) michael@0: break; michael@0: michael@0: } while (a || b); michael@0: michael@0: free(A2); michael@0: free(B2); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: michael@0: static void michael@0: TriggerQuirks() michael@0: { michael@0: int mib[2]; michael@0: michael@0: mib[0] = CTL_KERN; michael@0: mib[1] = KERN_OSRELEASE; michael@0: // we won't support versions greater than 10.7.99 michael@0: char release[sizeof("10.7.99")]; michael@0: size_t len = sizeof(release); michael@0: // sysctl will return ENOMEM if the release string is longer than sizeof(release) michael@0: int ret = sysctl(mib, 2, release, &len, nullptr, 0); michael@0: // we only want to trigger this on OS X 10.6, on versions 10.6.8 or newer michael@0: // Darwin version 10 corresponds to OS X version 10.6, version 11 is 10.7 michael@0: // http://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history michael@0: if (ret == 0 && NS_CompareVersions(release, "10.8.0") >= 0 && NS_CompareVersions(release, "11") < 0) { michael@0: CFBundleRef mainBundle = CFBundleGetMainBundle(); michael@0: if (mainBundle) { michael@0: CFRetain(mainBundle); michael@0: michael@0: CFStringRef bundleID = CFBundleGetIdentifier(mainBundle); michael@0: if (bundleID) { michael@0: CFRetain(bundleID); michael@0: michael@0: CFMutableDictionaryRef dict = (CFMutableDictionaryRef)CFBundleGetInfoDictionary(mainBundle); michael@0: CFDictionarySetValue(dict, CFSTR("CFBundleIdentifier"), CFSTR("org.mozilla.firefox")); michael@0: michael@0: // Trigger a load of the quirks table for org.mozilla.firefox. michael@0: // We use different function on 32/64bit because of how the APIs michael@0: // behave to force a call to GetBugsForOurBundleIDFromCoreservicesd. michael@0: #ifdef __i386__ michael@0: ProcessSerialNumber psn; michael@0: ::GetCurrentProcess(&psn); michael@0: #else michael@0: SInt32 major; michael@0: ::Gestalt(gestaltSystemVersionMajor, &major); michael@0: #endif michael@0: michael@0: // restore the original id michael@0: dict = (CFMutableDictionaryRef)CFBundleGetInfoDictionary(mainBundle); michael@0: CFDictionarySetValue(dict, CFSTR("CFBundleIdentifier"), bundleID); michael@0: michael@0: CFRelease(bundleID); michael@0: } michael@0: CFRelease(mainBundle); michael@0: } michael@0: } michael@0: } michael@0: michael@0: #endif //MacQuirks_h__