blob: f7d4e3376e8601547b49e29ebe44a79ea7943183 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "webkit/support/platform_support.h"
#import <AppKit/AppKit.h>
#include <AvailabilityMacros.h>
#import <Foundation/Foundation.h>
#import <objc/objc-runtime.h>
#include "base/base_paths.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/mac_util.h"
#include "base/path_service.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "grit/webkit_resources.h"
#include "ui/base/resource/data_pack.h"
#include "webkit/plugins/npapi/plugin_list.h"
#include "webkit/support/test_webkit_platform_support.h"
#import "webkit/support/drt_application_mac.h"
#import "webkit/tools/test_shell/mac/DumpRenderTreePasteboard.h"
static ui::DataPack* g_resource_data_pack = NULL;
namespace webkit_support {
static NSAutoreleasePool* autorelease_pool;
void BeforeInitialize(bool unit_test_mode) {
[CrDrtApplication sharedApplication];
autorelease_pool = [[NSAutoreleasePool alloc] init];
DCHECK(autorelease_pool);
}
#if OBJC_API_VERSION == 2
static void SwizzleAllMethods(Class imposter, Class original) {
unsigned int imposterMethodCount = 0;
Method* imposterMethods =
class_copyMethodList(imposter, &imposterMethodCount);
unsigned int originalMethodCount = 0;
Method* originalMethods =
class_copyMethodList(original, &originalMethodCount);
for (unsigned int i = 0; i < imposterMethodCount; i++) {
SEL imposterMethodName = method_getName(imposterMethods[i]);
// Attempt to add the method to the original class. If it fails, the method
// already exists and we should instead exchange the implementations.
if (class_addMethod(original,
imposterMethodName,
method_getImplementation(originalMethods[i]),
method_getTypeEncoding(originalMethods[i]))) {
continue;
}
unsigned int j = 0;
for (; j < originalMethodCount; j++) {
SEL originalMethodName = method_getName(originalMethods[j]);
if (sel_isEqual(imposterMethodName, originalMethodName)) {
break;
}
}
// If class_addMethod failed above then the method must exist on the
// original class.
DCHECK(j < originalMethodCount) << "method wasn't found?";
method_exchangeImplementations(imposterMethods[i], originalMethods[j]);
}
if (imposterMethods) {
free(imposterMethods);
}
if (originalMethods) {
free(originalMethods);
}
}
#endif
static void SwizzleNSPasteboard() {
// We replace NSPaseboard w/ the shim (from WebKit) that avoids having
// sideeffects w/ whatever the user does at the same time.
Class imposterClass = objc_getClass("DumpRenderTreePasteboard");
Class originalClass = objc_getClass("NSPasteboard");
#if OBJC_API_VERSION == 0
class_poseAs(imposterClass, originalClass);
#else
// Swizzle instance methods...
SwizzleAllMethods(imposterClass, originalClass);
// and then class methods.
SwizzleAllMethods(object_getClass(imposterClass),
object_getClass(originalClass));
#endif
}
void AfterInitialize(bool unit_test_mode) {
if (unit_test_mode)
return; // We don't have a resource pack when running the unit-tests.
// Load a data pack.
g_resource_data_pack = new ui::DataPack(ui::SCALE_FACTOR_100P);
NSString* resource_path =
[base::mac::FrameworkBundle() pathForResource:@"DumpRenderTree"
ofType:@"pak"];
FilePath resources_pak_path([resource_path fileSystemRepresentation]);
if (!g_resource_data_pack->Load(resources_pak_path)) {
LOG(FATAL) << "failed to load DumpRenderTree.pak";
}
// Load font files in the resource folder.
static const char* const fontFileNames[] = {
"AHEM____.TTF",
// We don't register WebKitWeightWather fonts because of
// webkit.org/b/50709.
};
#if defined(MAC_OS_X_VERSION_10_6) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
NSMutableArray* font_urls = [NSMutableArray array];
NSURL* resources_directory = [[NSBundle mainBundle] resourceURL];
for (unsigned i = 0; i < arraysize(fontFileNames); ++i) {
NSURL* font_url = [resources_directory
URLByAppendingPathComponent:[NSString
stringWithUTF8String:fontFileNames[i]]];
[font_urls addObject:[font_url absoluteURL]];
}
CFArrayRef errors = 0;
if (!CTFontManagerRegisterFontsForURLs((CFArrayRef)font_urls,
kCTFontManagerScopeProcess,
&errors)) {
DLOG(FATAL) << "Fail to activate fonts.";
CFRelease(errors);
}
#else
NSString* resources = [[NSBundle mainBundle] resourcePath];
for (unsigned i = 0; i < arraysize(fontFileNames); ++i) {
const char* resource_path = [[resources stringByAppendingPathComponent:
[NSString stringWithUTF8String:fontFileNames[i]]] UTF8String];
FSRef resource_ref;
const UInt8* uint8_resource_path
= reinterpret_cast<const UInt8*>(resource_path);
if (FSPathMakeRef(uint8_resource_path, &resource_ref, nil) != noErr) {
DLOG(FATAL) << "Fail to open " << resource_path;
}
if (ATSFontActivateFromFileReference(&resource_ref, kATSFontContextLocal,
kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, 0) != noErr) {
DLOG(FATAL) << "Fail to activate font: " << resource_path;
}
}
#endif
SwizzleNSPasteboard();
// Add <app bundle's parent dir>/plugins to the plugin path so we can load
// test plugins.
FilePath plugins_dir;
PathService::Get(base::DIR_EXE, &plugins_dir);
plugins_dir = plugins_dir.AppendASCII("../../../plugins");
webkit::npapi::PluginList::Singleton()->AddExtraPluginDir(plugins_dir);
}
void BeforeShutdown() {
}
void AfterShutdown() {
[DumpRenderTreePasteboard releaseLocalPasteboards];
[autorelease_pool drain];
delete g_resource_data_pack;
}
} // namespace webkit_support
string16 TestWebKitPlatformSupport::GetLocalizedString(int message_id) {
// |g_resource_data_pack| is null on unit tests.
// But som unit tests reach GetLocalizedString().
if (!g_resource_data_pack)
return string16();
base::StringPiece res;
if (!g_resource_data_pack->GetStringPiece(message_id, &res)) {
LOG(FATAL) << "failed to load webkit string with id " << message_id;
}
// Data packs hold strings as either UTF8 or UTF16.
string16 msg;
switch (g_resource_data_pack->GetTextEncodingType()) {
case ui::DataPack::UTF8:
msg = UTF8ToUTF16(res);
break;
case ui::DataPack::UTF16:
msg = string16(reinterpret_cast<const char16*>(res.data()),
res.length() / 2);
break;
case ui::DataPack::BINARY:
NOTREACHED();
break;
}
return msg;
}
// Helper method for getting the path to the test shell resources directory.
static FilePath GetResourcesFilePath() {
FilePath path;
// We assume the application is bundled.
if (!base::mac::AmIBundled()) {
LOG(FATAL) << "Failed to locate resources. The applicaiton is not bundled.";
}
PathService::Get(base::DIR_EXE, &path);
path = path.Append(FilePath::kParentDirectory);
return path.AppendASCII("Resources");
}
base::StringPiece TestWebKitPlatformSupport::GetDataResource(
int resource_id,
ui::ScaleFactor scale_factor) {
switch (resource_id) {
case IDR_BROKENIMAGE: {
// Use webkit's broken image icon (16x16)
CR_DEFINE_STATIC_LOCAL(std::string, broken_image_data, ());
if (broken_image_data.empty()) {
FilePath path = GetResourcesFilePath();
// In order to match WebKit's colors for the missing image, we have to
// use a PNG. The GIF doesn't have the color range needed to correctly
// match the TIFF they use in Safari.
path = path.AppendASCII("missingImage.png");
bool success = file_util::ReadFileToString(path, &broken_image_data);
if (!success) {
LOG(FATAL) << "Failed reading: " << path.value();
}
}
return broken_image_data;
}
case IDR_TEXTAREA_RESIZER: {
// Use webkit's text area resizer image.
CR_DEFINE_STATIC_LOCAL(std::string, resize_corner_data, ());
if (resize_corner_data.empty()) {
FilePath path = GetResourcesFilePath();
path = path.AppendASCII("textAreaResizeCorner.png");
bool success = file_util::ReadFileToString(path, &resize_corner_data);
if (!success) {
LOG(FATAL) << "Failed reading: " << path.value();
}
}
return resize_corner_data;
}
}
base::StringPiece res;
if (g_resource_data_pack)
g_resource_data_pack->GetStringPiece(resource_id, &res);
return res;
}