1 // Copyright 2014 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 "extensions/renderer/module_system_test.h" 6 7 #include <map> 8 #include <string> 9 10 #include "base/callback.h" 11 #include "base/files/file_path.h" 12 #include "base/files/file_util.h" 13 #include "base/lazy_instance.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "base/path_service.h" 16 #include "base/stl_util.h" 17 #include "base/strings/string_piece.h" 18 #include "extensions/common/extension_paths.h" 19 #include "extensions/renderer/logging_native_handler.h" 20 #include "extensions/renderer/object_backed_native_handler.h" 21 #include "extensions/renderer/safe_builtins.h" 22 #include "extensions/renderer/utils_native_handler.h" 23 #include "ui/base/resource/resource_bundle.h" 24 25 namespace extensions { 26 namespace { 27 28 class FailsOnException : public ModuleSystem::ExceptionHandler { 29 public: 30 virtual void HandleUncaughtException(const v8::TryCatch& try_catch) OVERRIDE { 31 FAIL() << "Uncaught exception: " << CreateExceptionString(try_catch); 32 } 33 }; 34 35 class V8ExtensionConfigurator { 36 public: 37 V8ExtensionConfigurator() 38 : safe_builtins_(SafeBuiltins::CreateV8Extension()), 39 names_(1, safe_builtins_->name()), 40 configuration_( 41 new v8::ExtensionConfiguration(static_cast<int>(names_.size()), 42 vector_as_array(&names_))) { 43 v8::RegisterExtension(safe_builtins_.get()); 44 } 45 46 v8::ExtensionConfiguration* GetConfiguration() { 47 return configuration_.get(); 48 } 49 50 private: 51 scoped_ptr<v8::Extension> safe_builtins_; 52 std::vector<const char*> names_; 53 scoped_ptr<v8::ExtensionConfiguration> configuration_; 54 }; 55 56 base::LazyInstance<V8ExtensionConfigurator>::Leaky g_v8_extension_configurator = 57 LAZY_INSTANCE_INITIALIZER; 58 59 } // namespace 60 61 // Native JS functions for doing asserts. 62 class ModuleSystemTestEnvironment::AssertNatives 63 : public ObjectBackedNativeHandler { 64 public: 65 explicit AssertNatives(ScriptContext* context) 66 : ObjectBackedNativeHandler(context), 67 assertion_made_(false), 68 failed_(false) { 69 RouteFunction( 70 "AssertTrue", 71 base::Bind(&AssertNatives::AssertTrue, base::Unretained(this))); 72 RouteFunction( 73 "AssertFalse", 74 base::Bind(&AssertNatives::AssertFalse, base::Unretained(this))); 75 } 76 77 bool assertion_made() { return assertion_made_; } 78 bool failed() { return failed_; } 79 80 void AssertTrue(const v8::FunctionCallbackInfo<v8::Value>& args) { 81 CHECK_EQ(1, args.Length()); 82 assertion_made_ = true; 83 failed_ = failed_ || !args[0]->ToBoolean()->Value(); 84 } 85 86 void AssertFalse(const v8::FunctionCallbackInfo<v8::Value>& args) { 87 CHECK_EQ(1, args.Length()); 88 assertion_made_ = true; 89 failed_ = failed_ || args[0]->ToBoolean()->Value(); 90 } 91 92 private: 93 bool assertion_made_; 94 bool failed_; 95 }; 96 97 // Source map that operates on std::strings. 98 class ModuleSystemTestEnvironment::StringSourceMap 99 : public ModuleSystem::SourceMap { 100 public: 101 StringSourceMap() {} 102 virtual ~StringSourceMap() {} 103 104 virtual v8::Handle<v8::Value> GetSource(v8::Isolate* isolate, 105 const std::string& name) OVERRIDE { 106 if (source_map_.count(name) == 0) 107 return v8::Undefined(isolate); 108 return v8::String::NewFromUtf8(isolate, source_map_[name].c_str()); 109 } 110 111 virtual bool Contains(const std::string& name) OVERRIDE { 112 return source_map_.count(name); 113 } 114 115 void RegisterModule(const std::string& name, const std::string& source) { 116 CHECK_EQ(0u, source_map_.count(name)) << "Module " << name << " not found"; 117 source_map_[name] = source; 118 } 119 120 private: 121 std::map<std::string, std::string> source_map_; 122 }; 123 124 ModuleSystemTestEnvironment::ModuleSystemTestEnvironment(v8::Isolate* isolate) 125 : isolate_(isolate), 126 context_holder_(new gin::ContextHolder(isolate_)), 127 handle_scope_(isolate_), 128 source_map_(new StringSourceMap()) { 129 context_holder_->SetContext(v8::Context::New( 130 isolate, g_v8_extension_configurator.Get().GetConfiguration())); 131 context_.reset(new ScriptContext(context_holder_->context(), 132 NULL, // WebFrame 133 NULL, // Extension 134 Feature::BLESSED_EXTENSION_CONTEXT, 135 NULL, // Effective Extension 136 Feature::BLESSED_EXTENSION_CONTEXT)); 137 context_->v8_context()->Enter(); 138 assert_natives_ = new AssertNatives(context_.get()); 139 140 { 141 scoped_ptr<ModuleSystem> module_system( 142 new ModuleSystem(context_.get(), source_map_.get())); 143 context_->set_module_system(module_system.Pass()); 144 } 145 ModuleSystem* module_system = context_->module_system(); 146 module_system->RegisterNativeHandler( 147 "assert", scoped_ptr<NativeHandler>(assert_natives_)); 148 module_system->RegisterNativeHandler( 149 "logging", 150 scoped_ptr<NativeHandler>(new LoggingNativeHandler(context_.get()))); 151 module_system->RegisterNativeHandler( 152 "utils", 153 scoped_ptr<NativeHandler>(new UtilsNativeHandler(context_.get()))); 154 module_system->SetExceptionHandlerForTest( 155 scoped_ptr<ModuleSystem::ExceptionHandler>(new FailsOnException)); 156 } 157 158 ModuleSystemTestEnvironment::~ModuleSystemTestEnvironment() { 159 if (context_) 160 context_->v8_context()->Exit(); 161 } 162 163 void ModuleSystemTestEnvironment::RegisterModule(const std::string& name, 164 const std::string& code) { 165 source_map_->RegisterModule(name, code); 166 } 167 168 void ModuleSystemTestEnvironment::RegisterModule(const std::string& name, 169 int resource_id) { 170 const std::string& code = ResourceBundle::GetSharedInstance() 171 .GetRawDataResource(resource_id) 172 .as_string(); 173 source_map_->RegisterModule(name, code); 174 } 175 176 void ModuleSystemTestEnvironment::OverrideNativeHandler( 177 const std::string& name, 178 const std::string& code) { 179 RegisterModule(name, code); 180 context_->module_system()->OverrideNativeHandlerForTest(name); 181 } 182 183 void ModuleSystemTestEnvironment::RegisterTestFile( 184 const std::string& module_name, 185 const std::string& file_name) { 186 base::FilePath test_js_file_path; 187 ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &test_js_file_path)); 188 test_js_file_path = test_js_file_path.AppendASCII(file_name); 189 std::string test_js; 190 ASSERT_TRUE(base::ReadFileToString(test_js_file_path, &test_js)); 191 source_map_->RegisterModule(module_name, test_js); 192 } 193 194 void ModuleSystemTestEnvironment::ShutdownGin() { 195 context_holder_.reset(); 196 } 197 198 void ModuleSystemTestEnvironment::ShutdownModuleSystem() { 199 context_->v8_context()->Exit(); 200 context_.reset(); 201 } 202 203 v8::Handle<v8::Object> ModuleSystemTestEnvironment::CreateGlobal( 204 const std::string& name) { 205 v8::EscapableHandleScope handle_scope(isolate_); 206 v8::Local<v8::Object> object = v8::Object::New(isolate_); 207 isolate_->GetCurrentContext()->Global()->Set( 208 v8::String::NewFromUtf8(isolate_, name.c_str()), object); 209 return handle_scope.Escape(object); 210 } 211 212 ModuleSystemTest::ModuleSystemTest() 213 : isolate_(v8::Isolate::GetCurrent()), 214 should_assertions_be_made_(true) { 215 } 216 217 ModuleSystemTest::~ModuleSystemTest() { 218 } 219 220 void ModuleSystemTest::SetUp() { 221 env_ = CreateEnvironment(); 222 } 223 224 void ModuleSystemTest::TearDown() { 225 // All tests must assert at least once unless otherwise specified. 226 EXPECT_EQ(should_assertions_be_made_, 227 env_->assert_natives()->assertion_made()); 228 EXPECT_FALSE(env_->assert_natives()->failed()); 229 env_.reset(); 230 v8::HeapStatistics stats; 231 isolate_->GetHeapStatistics(&stats); 232 size_t old_heap_size = 0; 233 // Run the GC until the heap size reaches a steady state to ensure that 234 // all the garbage is collected. 235 while (stats.used_heap_size() != old_heap_size) { 236 old_heap_size = stats.used_heap_size(); 237 isolate_->RequestGarbageCollectionForTesting( 238 v8::Isolate::kFullGarbageCollection); 239 isolate_->GetHeapStatistics(&stats); 240 } 241 } 242 243 scoped_ptr<ModuleSystemTestEnvironment> ModuleSystemTest::CreateEnvironment() { 244 return make_scoped_ptr(new ModuleSystemTestEnvironment(isolate_)); 245 } 246 247 void ModuleSystemTest::ExpectNoAssertionsMade() { 248 should_assertions_be_made_ = false; 249 } 250 251 void ModuleSystemTest::RunResolvedPromises() { 252 isolate_->RunMicrotasks(); 253 } 254 255 } // namespace extensions 256