| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/web_applications/web_app_mac.h" | 5 #import "chrome/browser/web_applications/web_app_mac.h" |
| 6 | 6 |
| 7 #import <Carbon/Carbon.h> | |
| 8 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 9 #include <stdint.h> | 8 #include <stdint.h> |
| 10 | 9 |
| 11 #include <utility> | 10 #include <utility> |
| 12 | 11 |
| 13 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 14 #include "base/files/file_enumerator.h" | 13 #include "base/files/file_enumerator.h" |
| 15 #include "base/files/file_util.h" | 14 #include "base/files/file_util.h" |
| 16 #include "base/files/scoped_temp_dir.h" | 15 #include "base/files/scoped_temp_dir.h" |
| 17 #include "base/mac/foundation_util.h" | 16 #include "base/mac/foundation_util.h" |
| (...skipping 29 matching lines...) Expand all Loading... |
| 47 #include "chrome/grit/generated_resources.h" | 46 #include "chrome/grit/generated_resources.h" |
| 48 #include "components/crx_file/id_util.h" | 47 #include "components/crx_file/id_util.h" |
| 49 #include "components/version_info/version_info.h" | 48 #include "components/version_info/version_info.h" |
| 50 #include "content/public/browser/browser_thread.h" | 49 #include "content/public/browser/browser_thread.h" |
| 51 #include "content/public/common/content_switches.h" | 50 #include "content/public/common/content_switches.h" |
| 52 #include "extensions/browser/extension_registry.h" | 51 #include "extensions/browser/extension_registry.h" |
| 53 #include "extensions/common/extension.h" | 52 #include "extensions/common/extension.h" |
| 54 #import "skia/ext/skia_utils_mac.h" | 53 #import "skia/ext/skia_utils_mac.h" |
| 55 #include "third_party/skia/include/core/SkBitmap.h" | 54 #include "third_party/skia/include/core/SkBitmap.h" |
| 56 #include "third_party/skia/include/core/SkColor.h" | 55 #include "third_party/skia/include/core/SkColor.h" |
| 56 #include "third_party/skia/include/utils/mac/SkCGUtils.h" |
| 57 #include "ui/base/l10n/l10n_util.h" | 57 #include "ui/base/l10n/l10n_util.h" |
| 58 #include "ui/base/resource/resource_bundle.h" | 58 #include "ui/base/resource/resource_bundle.h" |
| 59 #include "ui/gfx/image/image_family.h" | 59 #include "ui/gfx/image/image_family.h" |
| 60 | 60 |
| 61 bool g_app_shims_allow_update_and_launch_in_tests = false; | 61 bool g_app_shims_allow_update_and_launch_in_tests = false; |
| 62 | 62 |
| 63 namespace { | 63 namespace { |
| 64 | 64 |
| 65 // Launch Services Key to run as an agent app, which doesn't launch in the dock. | 65 // Launch Services Key to run as an agent app, which doesn't launch in the dock. |
| 66 NSString* const kLSUIElement = @"LSUIElement"; | 66 NSString* const kLSUIElement = @"LSUIElement"; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 86 content::BrowserThread::UI>; | 86 content::BrowserThread::UI>; |
| 87 | 87 |
| 88 ~Latch() { callback_.Run(); } | 88 ~Latch() { callback_.Run(); } |
| 89 void NoOp() {} | 89 void NoOp() {} |
| 90 | 90 |
| 91 base::Closure callback_; | 91 base::Closure callback_; |
| 92 | 92 |
| 93 DISALLOW_COPY_AND_ASSIGN(Latch); | 93 DISALLOW_COPY_AND_ASSIGN(Latch); |
| 94 }; | 94 }; |
| 95 | 95 |
| 96 class ScopedCarbonHandle { | 96 // Writes |icons| to |path| in .icns format. |
| 97 public: | 97 bool WriteIconsToFile(const std::vector<gfx::Image>& icons, |
| 98 ScopedCarbonHandle(size_t initial_size) : handle_(NewHandle(initial_size)) { | 98 const base::FilePath& path) { |
| 99 DCHECK(handle_); | 99 base::scoped_nsobject<NSMutableData> data( |
| 100 DCHECK_EQ(noErr, MemError()); | 100 [[NSMutableData alloc] initWithCapacity:0]); |
| 101 base::ScopedCFTypeRef<CGImageDestinationRef> image_destination( |
| 102 CGImageDestinationCreateWithData(base::mac::NSToCFCast(data), |
| 103 kUTTypeAppleICNS, icons.size(), |
| 104 nullptr)); |
| 105 DCHECK(image_destination); |
| 106 for (const gfx::Image& image : icons) { |
| 107 base::ScopedCFTypeRef<CGImageRef> cg_image(SkCreateCGImageRefWithColorspace( |
| 108 image.AsBitmap(), base::mac::GetSRGBColorSpace())); |
| 109 CGImageDestinationAddImage(image_destination, cg_image, nullptr); |
| 101 } | 110 } |
| 102 ~ScopedCarbonHandle() { DisposeHandle(handle_); } | 111 if (!CGImageDestinationFinalize(image_destination)) { |
| 103 | 112 NOTREACHED() << "CGImageDestinationFinalize failed."; |
| 104 Handle Get() { return handle_; } | 113 return false; |
| 105 char* Data() { return *handle_; } | |
| 106 size_t HandleSize() const { return GetHandleSize(handle_); } | |
| 107 | |
| 108 IconFamilyHandle GetAsIconFamilyHandle() { | |
| 109 return reinterpret_cast<IconFamilyHandle>(handle_); | |
| 110 } | 114 } |
| 111 | 115 return [data writeToFile:base::mac::FilePathToNSString(path) atomically:NO]; |
| 112 bool WriteDataToFile(const base::FilePath& path) { | |
| 113 NSData* data = [NSData dataWithBytes:Data() | |
| 114 length:HandleSize()]; | |
| 115 return [data writeToFile:base::mac::FilePathToNSString(path) | |
| 116 atomically:NO]; | |
| 117 } | |
| 118 | |
| 119 private: | |
| 120 Handle handle_; | |
| 121 }; | |
| 122 | |
| 123 void ConvertSkiaToARGB(const SkBitmap& bitmap, ScopedCarbonHandle* handle) { | |
| 124 CHECK_EQ(4u * bitmap.width() * bitmap.height(), handle->HandleSize()); | |
| 125 | |
| 126 char* argb = handle->Data(); | |
| 127 SkAutoLockPixels lock(bitmap); | |
| 128 for (int y = 0; y < bitmap.height(); ++y) { | |
| 129 for (int x = 0; x < bitmap.width(); ++x) { | |
| 130 SkColor pixel = bitmap.getColor(x, y); | |
| 131 argb[0] = SkColorGetA(pixel); | |
| 132 argb[1] = SkColorGetR(pixel); | |
| 133 argb[2] = SkColorGetG(pixel); | |
| 134 argb[3] = SkColorGetB(pixel); | |
| 135 argb += 4; | |
| 136 } | |
| 137 } | |
| 138 } | 116 } |
| 139 | 117 |
| 140 // Adds |image| to |icon_family|. Returns true on success, false on failure. | 118 // Returns true if |image| can be used for an icon resource. |
| 141 bool AddGfxImageToIconFamily(IconFamilyHandle icon_family, | 119 bool IsImageValidForIcon(const gfx::Image& image) { |
| 142 const gfx::Image& image) { | 120 if (image.IsEmpty()) |
| 121 return false; |
| 122 |
| 143 // When called via ShowCreateChromeAppShortcutsDialog the ImageFamily will | 123 // When called via ShowCreateChromeAppShortcutsDialog the ImageFamily will |
| 144 // have all the representations desired here for mac, from the kDesiredSizes | 124 // have all the representations desired here for mac, from the kDesiredSizes |
| 145 // array in web_app.cc. | 125 // array in web_app.cc. |
| 146 SkBitmap bitmap = image.AsBitmap(); | 126 SkBitmap bitmap = image.AsBitmap(); |
| 147 if (bitmap.colorType() != kN32_SkColorType || | 127 if (bitmap.colorType() != kN32_SkColorType || |
| 148 bitmap.width() != bitmap.height()) { | 128 bitmap.width() != bitmap.height()) { |
| 149 return false; | 129 return false; |
| 150 } | 130 } |
| 151 | 131 |
| 152 OSType icon_type; | |
| 153 switch (bitmap.width()) { | 132 switch (bitmap.width()) { |
| 154 case 512: | 133 case 512: |
| 155 icon_type = kIconServices512PixelDataARGB; | |
| 156 break; | |
| 157 case 256: | 134 case 256: |
| 158 icon_type = kIconServices256PixelDataARGB; | |
| 159 break; | |
| 160 case 128: | 135 case 128: |
| 161 icon_type = kIconServices128PixelDataARGB; | |
| 162 break; | |
| 163 case 48: | 136 case 48: |
| 164 icon_type = kIconServices48PixelDataARGB; | |
| 165 break; | |
| 166 case 32: | 137 case 32: |
| 167 icon_type = kIconServices32PixelDataARGB; | |
| 168 break; | |
| 169 case 16: | 138 case 16: |
| 170 icon_type = kIconServices16PixelDataARGB; | 139 return true; |
| 171 break; | |
| 172 default: | |
| 173 return false; | |
| 174 } | 140 } |
| 175 | 141 return false; |
| 176 ScopedCarbonHandle raw_data(bitmap.getSize()); | |
| 177 ConvertSkiaToARGB(bitmap, &raw_data); | |
| 178 OSErr result = SetIconFamilyData(icon_family, icon_type, raw_data.Get()); | |
| 179 DCHECK_EQ(noErr, result); | |
| 180 return result == noErr; | |
| 181 } | 142 } |
| 182 | 143 |
| 183 bool AppShimsDisabledForTest() { | 144 bool AppShimsDisabledForTest() { |
| 184 // Disable app shims in tests because shims created in ~/Applications will not | 145 // Disable app shims in tests because shims created in ~/Applications will not |
| 185 // be cleaned up. | 146 // be cleaned up. |
| 186 return base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType); | 147 return base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType); |
| 187 } | 148 } |
| 188 | 149 |
| 189 base::FilePath GetWritableApplicationsDirectory() { | 150 base::FilePath GetWritableApplicationsDirectory() { |
| 190 base::FilePath path; | 151 base::FilePath path; |
| (...skipping 778 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 969 NSString* localized_path = base::mac::FilePathToNSString( | 930 NSString* localized_path = base::mac::FilePathToNSString( |
| 970 localized_dir.Append("InfoPlist.strings")); | 931 localized_dir.Append("InfoPlist.strings")); |
| 971 return [strings_plist writeToFile:localized_path | 932 return [strings_plist writeToFile:localized_path |
| 972 atomically:YES]; | 933 atomically:YES]; |
| 973 } | 934 } |
| 974 | 935 |
| 975 bool WebAppShortcutCreator::UpdateIcon(const base::FilePath& app_path) const { | 936 bool WebAppShortcutCreator::UpdateIcon(const base::FilePath& app_path) const { |
| 976 if (info_->favicon.empty()) | 937 if (info_->favicon.empty()) |
| 977 return true; | 938 return true; |
| 978 | 939 |
| 979 ScopedCarbonHandle icon_family(0); | 940 std::vector<gfx::Image> valid_icons; |
| 980 bool image_added = false; | |
| 981 for (gfx::ImageFamily::const_iterator it = info_->favicon.begin(); | 941 for (gfx::ImageFamily::const_iterator it = info_->favicon.begin(); |
| 982 it != info_->favicon.end(); ++it) { | 942 it != info_->favicon.end(); ++it) { |
| 983 if (it->IsEmpty()) | 943 if (IsImageValidForIcon(*it)) |
| 984 continue; | 944 valid_icons.push_back(*it); |
| 985 | |
| 986 // Missing an icon size is not fatal so don't fail if adding the bitmap | |
| 987 // doesn't work. | |
| 988 if (!AddGfxImageToIconFamily(icon_family.GetAsIconFamilyHandle(), *it)) | |
| 989 continue; | |
| 990 | |
| 991 image_added = true; | |
| 992 } | 945 } |
| 993 | 946 if (valid_icons.empty()) |
| 994 if (!image_added) | |
| 995 return false; | 947 return false; |
| 996 | 948 |
| 997 base::FilePath resources_path = GetResourcesPath(app_path); | 949 base::FilePath resources_path = GetResourcesPath(app_path); |
| 998 if (!base::CreateDirectory(resources_path)) | 950 if (!base::CreateDirectory(resources_path)) |
| 999 return false; | 951 return false; |
| 1000 | 952 |
| 1001 return icon_family.WriteDataToFile(resources_path.Append("app.icns")); | 953 return WriteIconsToFile(valid_icons, resources_path.Append("app.icns")); |
| 1002 } | 954 } |
| 1003 | 955 |
| 1004 bool WebAppShortcutCreator::UpdateInternalBundleIdentifier() const { | 956 bool WebAppShortcutCreator::UpdateInternalBundleIdentifier() const { |
| 1005 NSString* plist_path = GetPlistPath(GetInternalShortcutPath()); | 957 NSString* plist_path = GetPlistPath(GetInternalShortcutPath()); |
| 1006 NSMutableDictionary* plist = ReadPlist(plist_path); | 958 NSMutableDictionary* plist = ReadPlist(plist_path); |
| 1007 | 959 |
| 1008 [plist setObject:base::SysUTF8ToNSString(GetInternalBundleIdentifier()) | 960 [plist setObject:base::SysUTF8ToNSString(GetInternalBundleIdentifier()) |
| 1009 forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; | 961 forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; |
| 1010 return [plist writeToFile:plist_path | 962 return [plist writeToFile:plist_path |
| 1011 atomically:YES]; | 963 atomically:YES]; |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1177 BuildShortcutInfoFromBundle(*it); | 1129 BuildShortcutInfoFromBundle(*it); |
| 1178 WebAppShortcutCreator shortcut_creator(it->DirName(), shortcut_info.get(), | 1130 WebAppShortcutCreator shortcut_creator(it->DirName(), shortcut_info.get(), |
| 1179 extensions::FileHandlersInfo()); | 1131 extensions::FileHandlersInfo()); |
| 1180 shortcut_creator.DeleteShortcuts(); | 1132 shortcut_creator.DeleteShortcuts(); |
| 1181 } | 1133 } |
| 1182 } | 1134 } |
| 1183 | 1135 |
| 1184 } // namespace internals | 1136 } // namespace internals |
| 1185 | 1137 |
| 1186 } // namespace web_app | 1138 } // namespace web_app |
| OLD | NEW |