15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#import <QuartzCore/QuartzCore.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#import <UIKit/UIKit.h>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/bundle_locations.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/foundation_util.h"
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/mac/scoped_nsobject.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/ref_counted_memory.h"
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/sys_string_conversions.h"
18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/synchronization/lock.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_handle.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ui {
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::FilePath GetResourcesPakFilePath(NSString* name, NSString* mac_locale) {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NSString *resource_path;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ([mac_locale length]) {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resource_path = [base::mac::FrameworkBundle() pathForResource:name
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           ofType:@"pak"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                      inDirectory:@""
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                  forLocalization:mac_locale];
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resource_path = [base::mac::FrameworkBundle() pathForResource:name
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           ofType:@"pak"];
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!resource_path) {
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Return just the name of the pak file.
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return base::FilePath(base::SysNSStringToUTF8(name) + ".pak");
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return base::FilePath([resource_path fileSystemRepresentation]);
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ResourceBundle::LoadCommonResources() {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddDataPackFromPath(GetResourcesPakFilePath(@"chrome", nil),
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      ui::SCALE_FACTOR_NONE);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsScaleFactorSupported(SCALE_FACTOR_100P)) {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_100_percent", nil),
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        SCALE_FACTOR_100P);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsScaleFactorSupported(SCALE_FACTOR_200P)) {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_200_percent", nil),
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        SCALE_FACTOR_200P);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // TODO(rohitrao): Add a chrome_300_percent file and load it here.  For now,
611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // we are simply falling back to the 200P resources. http://crbug.com/413300.
621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (IsScaleFactorSupported(SCALE_FACTOR_300P)) {
631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_200_percent", nil),
641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                        SCALE_FACTOR_200P);
651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale,
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 bool test_file_exists) {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NSString* mac_locale = base::SysUTF8ToNSString(app_locale);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // iOS uses "_" instead of "-", so swap to get a iOS-style value.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mac_locale = [mac_locale stringByReplacingOccurrencesOfString:@"-"
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     withString:@"_"];
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // On disk, the "en_US" resources are just "en" (http://crbug.com/25578).
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ([mac_locale isEqual:@"en_US"])
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mac_locale = @"en";
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath locale_file_path =
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GetResourcesPakFilePath(@"locale", mac_locale);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (delegate_) {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    locale_file_path =
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delegate_->GetPathForLocalePack(locale_file_path, app_locale);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't try to load empty values or values that are not absolute paths.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (locale_file_path.empty() || !locale_file_path.IsAbsolute())
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return base::FilePath();
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (test_file_exists && !base::PathExists(locale_file_path))
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return base::FilePath();
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return locale_file_path;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Flipped images are not used on iOS.
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(rtl, RTL_DISABLED);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check to see if the image is already in the cache.
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::AutoLock lock(*images_and_fonts_lock_);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageMap::iterator found = images_.find(resource_id);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (found != images_.end()) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return found->second;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Image image;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (delegate_)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    image = delegate_->GetNativeImageNamed(resource_id, rtl);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (image.IsEmpty()) {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Load the raw data from the resource pack at the current supported scale
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // factor.  This code assumes that only one of the possible scale factors is
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // supported at runtime, based on the device resolution.
11968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    ui::ScaleFactor scale_factor = GetMaxScaleFactor();
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_refptr<base::RefCountedStaticMemory> data(
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        LoadDataResourceBytesForScale(resource_id, scale_factor));
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!data.get()) {
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      LOG(WARNING) << "Unable to load image with id " << resource_id;
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return GetEmptyImage();
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Create a data object from the raw bytes.
130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    base::scoped_nsobject<NSData> ns_data(
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        [[NSData alloc] initWithBytes:data->front() length:data->size()]);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bool is_fallback = PNGContainsFallbackMarker(data->front(), data->size());
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Create the image from the data.
135f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    CGFloat target_scale = ui::GetScaleForScaleFactor(scale_factor);
1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Hack: The 200P pak file is the only pak file loaded on iOS devices with
1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // an @3x scale factor.  Force |source_scale| to be 2.0 to handle this case,
1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // since it cannot be anything else.  http://crbug.com/413300.
1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // TODO(rohitrao): Support proper fallback by using the actual scale factor
1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // of the source image, rather than assuming it is 1.0 or 2.0.
1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    CGFloat source_scale = target_scale;
1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (is_fallback) {
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      source_scale = 1.0;
1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    } else if (scale_factor == SCALE_FACTOR_300P) {
1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      source_scale = 2.0;
1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    base::scoped_nsobject<UIImage> ui_image(
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        [[UIImage alloc] initWithData:ns_data scale:source_scale]);
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // If the image is a 1x fallback, scale it up to a full-size representation.
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (is_fallback) {
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CGSize source_size = [ui_image size];
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CGSize target_size = CGSizeMake(source_size.width * target_scale,
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      source_size.height * target_scale);
155eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          CGColorSpaceCreateDeviceRGB());
157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          NULL,
159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          target_size.width,
160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          target_size.height,
161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          8,
162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          target_size.width * 4,
163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          color_space,
164eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CGRect target_rect = CGRectMake(0, 0,
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      target_size.width, target_size.height);
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CGContextSetBlendMode(context, kCGBlendModeCopy);
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CGContextDrawImage(context, target_rect, [ui_image CGImage]);
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
171eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      base::ScopedCFTypeRef<CGImageRef> cg_image(
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          CGBitmapContextCreateImage(context));
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ui_image.reset([[UIImage alloc] initWithCGImage:cg_image
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                scale:target_scale
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                          orientation:UIImageOrientationUp]);
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!ui_image.get()) {
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(WARNING) << "Unable to load image with id " << resource_id;
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();  // Want to assert in debug mode.
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return GetEmptyImage();
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The gfx::Image takes ownership.
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    image = gfx::Image(ui_image.release());
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::AutoLock lock(*images_and_fonts_lock_);
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Another thread raced the load and has already cached the image.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (images_.count(resource_id))
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return images_[resource_id];
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  images_[resource_id] = image;
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return images_[resource_id];
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace ui
199