Revert 168224 - Update V8 to version 3.15.4.
[chromium-blink-merge.git] / chrome / browser / web_applications / web_app_mac.mm
blob2ab85608fd6ec1ce91e4c5433735d713e82bc2ed
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
3 // found in the LICENSE file.
5 #import "chrome/browser/web_applications/web_app_mac.h"
7 #import <Cocoa/Cocoa.h>
9 #include "base/file_util.h"
10 #include "base/mac/bundle_locations.h"
11 #include "base/mac/foundation_util.h"
12 #include "base/mac/mac_util.h"
13 #include "base/memory/scoped_nsobject.h"
14 #include "base/scoped_temp_dir.h"
15 #include "base/sys_string_conversions.h"
16 #include "base/utf_string_conversions.h"
17 #include "chrome/browser/web_applications/web_app.h"
18 #include "chrome/common/chrome_paths_internal.h"
19 #include "chrome/common/mac/app_mode_common.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "grit/chromium_strings.h"
22 #include "skia/ext/skia_utils_mac.h"
23 #include "third_party/icon_family/IconFamily.h"
24 #include "ui/base/l10n/l10n_util_mac.h"
25 #include "ui/gfx/image/image_skia.h"
27 namespace {
29 // Creates a NSBitmapImageRep from |bitmap|.
30 NSBitmapImageRep* SkBitmapToImageRep(const SkBitmap& bitmap) {
31   base::mac::ScopedCFTypeRef<CGColorSpaceRef> color_space(
32       CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
33   NSImage* image = gfx::SkBitmapToNSImageWithColorSpace(
34     bitmap, color_space.get());
35   return base::mac::ObjCCast<NSBitmapImageRep>(
36       [[image representations] lastObject]);
39 // Adds |image_rep| to |icon_family|. Returns true on success, false on failure.
40 bool AddBitmapImageRepToIconFamily(IconFamily* icon_family,
41                                    NSBitmapImageRep* image_rep) {
42   NSSize size = [image_rep size];
43   if (size.width != size.height)
44     return false;
46   switch (static_cast<int>(size.width)) {
47     case 512:
48       return [icon_family setIconFamilyElement:kIconServices512PixelDataARGB
49                             fromBitmapImageRep:image_rep];
50     case 256:
51       return [icon_family setIconFamilyElement:kIconServices256PixelDataARGB
52                             fromBitmapImageRep:image_rep];
53     case 128:
54       return [icon_family setIconFamilyElement:kThumbnail32BitData
55                             fromBitmapImageRep:image_rep] &&
56              [icon_family setIconFamilyElement:kThumbnail8BitMask
57                             fromBitmapImageRep:image_rep];
58     case 32:
59       return [icon_family setIconFamilyElement:kLarge32BitData
60                             fromBitmapImageRep:image_rep] &&
61              [icon_family setIconFamilyElement:kLarge8BitData
62                             fromBitmapImageRep:image_rep] &&
63              [icon_family setIconFamilyElement:kLarge8BitMask
64                             fromBitmapImageRep:image_rep] &&
65              [icon_family setIconFamilyElement:kLarge1BitMask
66                             fromBitmapImageRep:image_rep];
67     case 16:
68       return [icon_family setIconFamilyElement:kSmall32BitData
69                             fromBitmapImageRep:image_rep] &&
70              [icon_family setIconFamilyElement:kSmall8BitData
71                             fromBitmapImageRep:image_rep] &&
72              [icon_family setIconFamilyElement:kSmall8BitMask
73                             fromBitmapImageRep:image_rep] &&
74              [icon_family setIconFamilyElement:kSmall1BitMask
75                             fromBitmapImageRep:image_rep];
76     default:
77       return false;
78   }
81 }  // namespace
84 namespace web_app {
86 WebAppShortcutCreator::WebAppShortcutCreator(
87     const FilePath& user_data_dir,
88     const ShellIntegration::ShortcutInfo& shortcut_info,
89     const string16& chrome_bundle_id)
90     : user_data_dir_(user_data_dir),
91       info_(shortcut_info),
92       chrome_bundle_id_(chrome_bundle_id) {
95 WebAppShortcutCreator::~WebAppShortcutCreator() {
98 bool WebAppShortcutCreator::CreateShortcut() {
99   FilePath app_name = internals::GetSanitizedFileName(info_.title);
100   FilePath app_file_name = app_name.ReplaceExtension("app");
101   ScopedTempDir scoped_temp_dir;
102   if (!scoped_temp_dir.CreateUniqueTempDir())
103     return false;
104   FilePath staging_path = scoped_temp_dir.path().Append(app_file_name);
106   // Update the app's plist and icon in a temp directory. This works around
107   // a Finder bug where the app's icon doesn't properly update.
108   if (!file_util::CopyDirectory(GetAppLoaderPath(), staging_path, true)) {
109     LOG(ERROR) << "Copying app to staging path: " << staging_path.value()
110                << " failed";
111     return false;
112   }
114   if (!UpdatePlist(staging_path))
115     return false;
117   if (!UpdateIcon(staging_path))
118     return false;
120   FilePath dst_path = GetDestinationPath(app_file_name);
121   if (!file_util::CopyDirectory(staging_path, dst_path, true)) {
122     LOG(ERROR) << "Copying app to dst path: " << dst_path.value() << " failed";
123     return false;
124   }
126   RevealGeneratedBundleInFinder(dst_path);
128   return true;
131 FilePath WebAppShortcutCreator::GetAppLoaderPath() const {
132   return base::mac::PathForFrameworkBundleResource(
133       base::mac::NSToCFCast(@"app_mode_loader.app"));
136 FilePath WebAppShortcutCreator::GetDestinationPath(
137     const FilePath& app_file_name) const {
138   FilePath path;
139   if (base::mac::GetLocalDirectory(NSApplicationDirectory, &path) &&
140       file_util::PathIsWritable(path)) {
141     return path;
142   }
144   if (base::mac::GetUserDirectory(NSApplicationDirectory, &path))
145     return path;
147   return FilePath();
150 bool WebAppShortcutCreator::UpdatePlist(const FilePath& app_path) const {
151   NSString* plist_path = base::mac::FilePathToNSString(
152       app_path.Append("Contents").Append("Info.plist"));
154   NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id);
155   NSString* extension_title = base::SysUTF16ToNSString(info_.title);
156   NSString* extension_url = base::SysUTF8ToNSString(info_.url.spec());
157   NSString* chrome_bundle_id = base::SysUTF16ToNSString(chrome_bundle_id_);
158   NSDictionary* replacement_dict =
159       [NSDictionary dictionaryWithObjectsAndKeys:
160           extension_id, app_mode::kShortcutIdPlaceholder,
161           extension_title, app_mode::kShortcutNamePlaceholder,
162           extension_url, app_mode::kShortcutURLPlaceholder,
163           chrome_bundle_id, app_mode::kShortcutBrowserBundleIDPlaceholder,
164           nil];
166   NSMutableDictionary* plist =
167       [NSMutableDictionary dictionaryWithContentsOfFile:plist_path];
168   NSArray* keys = [plist allKeys];
170   // 1. Fill in variables.
171   for (id key in keys) {
172     NSString* value = [plist valueForKey:key];
173     if (![value isKindOfClass:[NSString class]] || [value length] < 2)
174       continue;
176     // Remove leading and trailing '@'s.
177     NSString* variable =
178         [value substringWithRange:NSMakeRange(1, [value length] - 2)];
180     NSString* substitution = [replacement_dict valueForKey:variable];
181     if (substitution)
182       [plist setObject:substitution forKey:key];
183   }
185   // 2. Fill in other values.
186   [plist setObject:GetBundleIdentifier(plist)
187             forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)];
188   [plist setObject:base::mac::FilePathToNSString(user_data_dir_)
189             forKey:app_mode::kCrAppModeUserDataDirKey];
190   [plist setObject:base::mac::FilePathToNSString(info_.extension_path)
191             forKey:app_mode::kCrAppModeExtensionPathKey];
192   return [plist writeToFile:plist_path atomically:YES];
195 bool WebAppShortcutCreator::UpdateIcon(const FilePath& app_path) const {
196   if (info_.favicon.IsEmpty())
197     return true;
199   scoped_nsobject<IconFamily> icon_family([[IconFamily alloc] init]);
200   bool image_added = false;
201   info_.favicon.ToImageSkia()->EnsureRepsForSupportedScaleFactors();
202   std::vector<gfx::ImageSkiaRep> image_reps =
203       info_.favicon.ToImageSkia()->image_reps();
204   for (size_t i = 0; i < image_reps.size(); ++i) {
205     NSBitmapImageRep* image_rep = SkBitmapToImageRep(
206         image_reps[i].sk_bitmap());
207     if (!image_rep)
208       continue;
210     // Missing an icon size is not fatal so don't fail if adding the bitmap
211     // doesn't work.
212     if (!AddBitmapImageRepToIconFamily(icon_family, image_rep))
213       continue;
215     image_added = true;
216   }
218   if (!image_added)
219     return false;
221   FilePath resources_path = app_path.Append("Contents").Append("Resources");
222   if (!file_util::CreateDirectory(resources_path))
223     return false;
224   FilePath icon_path = resources_path.Append("app.icns");
225   return [icon_family writeToFile:base::mac::FilePathToNSString(icon_path)];
228 NSString* WebAppShortcutCreator::GetBundleIdentifier(NSDictionary* plist) const
230   NSString* bundle_id_template =
231     base::mac::ObjCCast<NSString>(
232         [plist objectForKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]);
233   NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id);
234   NSString* placeholder =
235       [NSString stringWithFormat:@"@%@@", app_mode::kShortcutIdPlaceholder];
236   NSString* bundle_id =
237       [bundle_id_template
238           stringByReplacingOccurrencesOfString:placeholder
239                                     withString:extension_id];
240   return bundle_id;
243 void WebAppShortcutCreator::RevealGeneratedBundleInFinder(
244     const FilePath& generated_bundle) const {
245   [[NSWorkspace sharedWorkspace]
246                     selectFile:base::mac::FilePathToNSString(generated_bundle)
247       inFileViewerRootedAtPath:nil];
250 }  // namespace
252 namespace web_app {
254 namespace internals {
256 bool CreatePlatformShortcuts(
257     const FilePath& web_app_path,
258     const ShellIntegration::ShortcutInfo& shortcut_info) {
259   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
260   string16 bundle_id = UTF8ToUTF16(base::mac::BaseBundleID());
261   WebAppShortcutCreator shortcut_creator(web_app_path, shortcut_info,
262                             bundle_id);
263   return shortcut_creator.CreateShortcut();
266 void DeletePlatformShortcuts(
267     const FilePath& web_app_path,
268     const ShellIntegration::ShortcutInfo& shortcut_info) {
269   // TODO(benwells): Implement this when shortcuts / weblings are enabled on
270   // mac.
273 void UpdatePlatformShortcuts(
274     const FilePath& web_app_path,
275     const ShellIntegration::ShortcutInfo& shortcut_info) {
276   // TODO(benwells): Implement this when shortcuts / weblings are enabled on
277   // mac.
280 }  // namespace internals
282 }  // namespace web_app