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.
4
5#include "ui/base/resource/resource_bundle.h"
6
7#import <AppKit/AppKit.h>
8#include <stddef.h>
9
10#include "base/files/file_path.h"
11#include "base/files/file_util.h"
12#include "base/logging.h"
13#include "base/mac/bundle_locations.h"
14#include "base/mac/scoped_nsobject.h"
15#include "base/memory/ref_counted_memory.h"
16#include "base/notreached.h"
17#include "base/strings/sys_string_conversions.h"
18#include "base/synchronization/lock.h"
19#include "ui/base/resource/resource_handle.h"
20#include "ui/gfx/image/image.h"
21
22namespace ui {
23
24namespace {
25
26base::FilePath GetResourcesPakFilePath(NSString* name, NSString* mac_locale) {
27  NSString *resource_path;
28  // Some of the helper processes need to be able to fetch resources
29  // (chrome_main.cc: SubprocessNeedsResourceBundle()). Fetch the same locale
30  // as the already-running browser instead of using what NSBundle might pick
31  // based on values at helper launch time.
32  if ([mac_locale length]) {
33    resource_path = [base::mac::FrameworkBundle() pathForResource:name
34                                                           ofType:@"pak"
35                                                      inDirectory:@""
36                                                  forLocalization:mac_locale];
37  } else {
38    resource_path = [base::mac::FrameworkBundle() pathForResource:name
39                                                           ofType:@"pak"];
40  }
41
42  if (!resource_path) {
43    // Return just the name of the pack file.
44    return base::FilePath(base::SysNSStringToUTF8(name) + ".pak");
45  }
46
47  return base::FilePath([resource_path fileSystemRepresentation]);
48}
49
50}  // namespace
51
52void ResourceBundle::LoadCommonResources() {
53  AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_100_percent",
54                        nil), SCALE_FACTOR_100P);
55
56  // On Mac we load 1x and 2x resources and we let the UI framework decide
57  // which one to use.
58  if (IsScaleFactorSupported(SCALE_FACTOR_200P)) {
59    AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_200_percent", nil),
60                        SCALE_FACTOR_200P);
61  }
62}
63
64// static
65base::FilePath ResourceBundle::GetLocaleFilePath(
66    const std::string& app_locale) {
67  NSString* mac_locale = base::SysUTF8ToNSString(app_locale);
68
69  // Mac OS X uses "_" instead of "-", so swap to get a Mac-style value.
70  mac_locale = [mac_locale stringByReplacingOccurrencesOfString:@"-"
71                                                     withString:@"_"];
72
73  // On disk, the "en_US" resources are just "en" (http://crbug.com/25578).
74  if ([mac_locale isEqual:@"en_US"])
75    mac_locale = @"en";
76
77  base::FilePath locale_file_path =
78      GetResourcesPakFilePath(@"locale", mac_locale);
79
80  if (HasSharedInstance() && GetSharedInstance().delegate_) {
81    locale_file_path = GetSharedInstance().delegate_->GetPathForLocalePack(
82        locale_file_path, app_locale);
83  }
84
85  // Don't try to load from paths that are not absolute.
86  return locale_file_path.IsAbsolute() ? locale_file_path : base::FilePath();
87}
88
89gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) {
90  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
91  // Check to see if the image is already in the cache.
92  auto found = images_.find(resource_id);
93  if (found != images_.end()) {
94    if (!found->second.HasRepresentation(gfx::Image::kImageRepCocoa)) {
95      DLOG(WARNING)
96          << "ResourceBundle::GetNativeImageNamed() is returning a"
97          << " cached gfx::Image that isn't backed by an NSImage. The image"
98          << " will be converted, rather than going through the NSImage loader."
99          << " resource_id = " << resource_id;
100    }
101    return found->second;
102  }
103
104  gfx::Image image;
105  if (delegate_)
106    image = delegate_->GetNativeImageNamed(resource_id);
107
108  if (image.IsEmpty()) {
109    base::scoped_nsobject<NSImage> ns_image;
110    for (size_t i = 0; i < data_packs_.size(); ++i) {
111      scoped_refptr<base::RefCountedStaticMemory> data(
112          data_packs_[i]->GetStaticMemory(resource_id));
113      if (!data.get())
114        continue;
115
116      base::scoped_nsobject<NSData> ns_data(
117          [[NSData alloc] initWithBytes:data->front() length:data->size()]);
118      if (!ns_image.get()) {
119        ns_image.reset([[NSImage alloc] initWithData:ns_data]);
120      } else {
121        NSImageRep* image_rep = [NSBitmapImageRep imageRepWithData:ns_data];
122        if (image_rep)
123          [ns_image addRepresentation:image_rep];
124      }
125    }
126
127    if (!ns_image.get()) {
128      LOG(WARNING) << "Unable to load image with id " << resource_id;
129      NOTREACHED();  // Want to assert in debug mode.
130      return GetEmptyImage();
131    }
132
133    image = gfx::Image(ns_image);
134  }
135
136  auto inserted = images_.emplace(resource_id, image);
137  DCHECK(inserted.second);
138  return inserted.first->second;
139}
140
141}  // namespace ui
142