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 #include "webkit/support/platform_support.h"
7 #import <AppKit/AppKit.h>
8 #include <AvailabilityMacros.h>
9 #import <Foundation/Foundation.h>
10 #import <objc/objc-runtime.h>
12 #include "base/base_paths.h"
13 #include "base/file_util.h"
14 #include "base/logging.h"
15 #include "base/mac/bundle_locations.h"
16 #include "base/mac/mac_util.h"
17 #include "base/path_service.h"
18 #include "base/string16.h"
19 #include "base/utf_string_conversions.h"
20 #include "grit/webkit_resources.h"
21 #include "ui/base/resource/data_pack.h"
22 #include "webkit/plugins/npapi/plugin_list.h"
23 #include "webkit/support/test_webkit_platform_support.h"
24 #import "webkit/support/drt_application_mac.h"
25 #import "webkit/tools/test_shell/mac/DumpRenderTreePasteboard.h"
27 static ui::DataPack* g_resource_data_pack = NULL;
29 namespace webkit_support {
31 static NSAutoreleasePool* autorelease_pool;
33 void BeforeInitialize(bool unit_test_mode) {
34 [CrDrtApplication sharedApplication];
35 autorelease_pool = [[NSAutoreleasePool alloc] init];
36 DCHECK(autorelease_pool);
39 #if OBJC_API_VERSION == 2
40 static void SwizzleAllMethods(Class imposter, Class original) {
41 unsigned int imposterMethodCount = 0;
42 Method* imposterMethods =
43 class_copyMethodList(imposter, &imposterMethodCount);
45 unsigned int originalMethodCount = 0;
46 Method* originalMethods =
47 class_copyMethodList(original, &originalMethodCount);
49 for (unsigned int i = 0; i < imposterMethodCount; i++) {
50 SEL imposterMethodName = method_getName(imposterMethods[i]);
52 // Attempt to add the method to the original class. If it fails, the method
53 // already exists and we should instead exchange the implementations.
54 if (class_addMethod(original,
56 method_getImplementation(originalMethods[i]),
57 method_getTypeEncoding(originalMethods[i]))) {
62 for (; j < originalMethodCount; j++) {
63 SEL originalMethodName = method_getName(originalMethods[j]);
64 if (sel_isEqual(imposterMethodName, originalMethodName)) {
69 // If class_addMethod failed above then the method must exist on the
71 DCHECK(j < originalMethodCount) << "method wasn't found?";
72 method_exchangeImplementations(imposterMethods[i], originalMethods[j]);
75 if (imposterMethods) {
76 free(imposterMethods);
78 if (originalMethods) {
79 free(originalMethods);
84 static void SwizzleNSPasteboard() {
85 // We replace NSPaseboard w/ the shim (from WebKit) that avoids having
86 // sideeffects w/ whatever the user does at the same time.
88 Class imposterClass = objc_getClass("DumpRenderTreePasteboard");
89 Class originalClass = objc_getClass("NSPasteboard");
90 #if OBJC_API_VERSION == 0
91 class_poseAs(imposterClass, originalClass);
93 // Swizzle instance methods...
94 SwizzleAllMethods(imposterClass, originalClass);
95 // and then class methods.
96 SwizzleAllMethods(object_getClass(imposterClass),
97 object_getClass(originalClass));
101 void AfterInitialize(bool unit_test_mode) {
103 g_resource_data_pack = new ui::DataPack(ui::SCALE_FACTOR_100P);
104 FilePath resources_pak_path;
105 if (unit_test_mode) {
106 PathService::Get(base::DIR_EXE, &resources_pak_path);
107 resources_pak_path = resources_pak_path.Append("DumpRenderTree.app")
110 .Append("DumpRenderTree.pak");
112 NSString* resource_path =
113 [base::mac::FrameworkBundle() pathForResource:@"DumpRenderTree"
115 resources_pak_path = FilePath([resource_path fileSystemRepresentation]);
117 if (!g_resource_data_pack->LoadFromPath(resources_pak_path)) {
118 LOG(FATAL) << "failed to load DumpRenderTree.pak";
124 // Load font files in the resource folder.
125 static const char* const fontFileNames[] = {
127 "WebKitWeightWatcher100.ttf",
128 "WebKitWeightWatcher200.ttf",
129 "WebKitWeightWatcher300.ttf",
130 "WebKitWeightWatcher400.ttf",
131 "WebKitWeightWatcher500.ttf",
132 "WebKitWeightWatcher600.ttf",
133 "WebKitWeightWatcher700.ttf",
134 "WebKitWeightWatcher800.ttf",
135 "WebKitWeightWatcher900.ttf",
138 NSMutableArray* font_urls = [NSMutableArray array];
139 NSURL* resources_directory = [[NSBundle mainBundle] resourceURL];
140 for (unsigned i = 0; i < arraysize(fontFileNames); ++i) {
141 NSURL* font_url = [resources_directory
142 URLByAppendingPathComponent:[NSString
143 stringWithUTF8String:fontFileNames[i]]];
144 [font_urls addObject:[font_url absoluteURL]];
147 CFArrayRef errors = 0;
148 if (!CTFontManagerRegisterFontsForURLs((CFArrayRef)font_urls,
149 kCTFontManagerScopeProcess,
151 DLOG(FATAL) << "Fail to activate fonts.";
155 SwizzleNSPasteboard();
157 // Add <app bundle's parent dir>/plugins to the plugin path so we can load
159 FilePath plugins_dir;
160 PathService::Get(base::DIR_EXE, &plugins_dir);
161 plugins_dir = plugins_dir.AppendASCII("../../../plugins");
162 webkit::npapi::PluginList::Singleton()->AddExtraPluginDir(plugins_dir);
165 void BeforeShutdown() {
168 void AfterShutdown() {
169 [DumpRenderTreePasteboard releaseLocalPasteboards];
170 [autorelease_pool drain];
171 delete g_resource_data_pack;
174 } // namespace webkit_support
176 string16 TestWebKitPlatformSupport::GetLocalizedString(int message_id) {
177 // |g_resource_data_pack| is null on unit tests.
178 // But som unit tests reach GetLocalizedString().
179 if (!g_resource_data_pack)
181 base::StringPiece res;
182 if (!g_resource_data_pack->GetStringPiece(message_id, &res)) {
183 LOG(FATAL) << "failed to load webkit string with id " << message_id;
186 // Data packs hold strings as either UTF8 or UTF16.
188 switch (g_resource_data_pack->GetTextEncodingType()) {
189 case ui::DataPack::UTF8:
190 msg = UTF8ToUTF16(res);
192 case ui::DataPack::UTF16:
193 msg = string16(reinterpret_cast<const char16*>(res.data()),
196 case ui::DataPack::BINARY:
204 // Helper method for getting the path to the test shell resources directory.
205 static FilePath GetResourcesFilePath() {
207 // We assume the application is bundled.
208 if (!base::mac::AmIBundled()) {
209 LOG(FATAL) << "Failed to locate resources. The applicaiton is not bundled.";
211 PathService::Get(base::DIR_EXE, &path);
212 path = path.Append(FilePath::kParentDirectory);
213 return path.AppendASCII("Resources");
216 base::StringPiece TestWebKitPlatformSupport::GetDataResource(
218 ui::ScaleFactor scale_factor) {
219 switch (resource_id) {
220 case IDR_BROKENIMAGE: {
221 // Use webkit's broken image icon (16x16)
222 CR_DEFINE_STATIC_LOCAL(std::string, broken_image_data, ());
223 if (broken_image_data.empty()) {
224 FilePath path = GetResourcesFilePath();
225 // In order to match WebKit's colors for the missing image, we have to
226 // use a PNG. The GIF doesn't have the color range needed to correctly
227 // match the TIFF they use in Safari.
228 path = path.AppendASCII("missingImage.png");
229 bool success = file_util::ReadFileToString(path, &broken_image_data);
231 LOG(FATAL) << "Failed reading: " << path.value();
234 return broken_image_data;
236 case IDR_TEXTAREA_RESIZER: {
237 // Use webkit's text area resizer image.
238 CR_DEFINE_STATIC_LOCAL(std::string, resize_corner_data, ());
239 if (resize_corner_data.empty()) {
240 FilePath path = GetResourcesFilePath();
241 path = path.AppendASCII("textAreaResizeCorner.png");
242 bool success = file_util::ReadFileToString(path, &resize_corner_data);
244 LOG(FATAL) << "Failed reading: " << path.value();
247 return resize_corner_data;
250 base::StringPiece res;
251 if (g_resource_data_pack)
252 g_resource_data_pack->GetStringPiece(resource_id, &res);