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 "base/memory/scoped_ptr.h" 6#include "extensions/renderer/module_system.h" 7#include "extensions/renderer/module_system_test.h" 8#include "gin/modules/module_registry.h" 9 10namespace extensions { 11 12class CounterNatives : public ObjectBackedNativeHandler { 13 public: 14 explicit CounterNatives(ScriptContext* context) 15 : ObjectBackedNativeHandler(context), counter_(0) { 16 RouteFunction("Get", 17 base::Bind(&CounterNatives::Get, base::Unretained(this))); 18 RouteFunction( 19 "Increment", 20 base::Bind(&CounterNatives::Increment, base::Unretained(this))); 21 } 22 23 void Get(const v8::FunctionCallbackInfo<v8::Value>& args) { 24 args.GetReturnValue().Set(static_cast<int32_t>(counter_)); 25 } 26 27 void Increment(const v8::FunctionCallbackInfo<v8::Value>& args) { 28 counter_++; 29 } 30 31 private: 32 int counter_; 33}; 34 35class TestExceptionHandler : public ModuleSystem::ExceptionHandler { 36 public: 37 TestExceptionHandler() : handled_exception_(false) {} 38 39 virtual void HandleUncaughtException(const v8::TryCatch& try_catch) OVERRIDE { 40 handled_exception_ = true; 41 } 42 43 bool handled_exception() const { return handled_exception_; } 44 45 private: 46 bool handled_exception_; 47}; 48 49TEST_F(ModuleSystemTest, TestExceptionHandling) { 50 ModuleSystem::NativesEnabledScope natives_enabled_scope( 51 env()->module_system()); 52 TestExceptionHandler* handler = new TestExceptionHandler; 53 scoped_ptr<ModuleSystem::ExceptionHandler> scoped_handler(handler); 54 ASSERT_FALSE(handler->handled_exception()); 55 env()->module_system()->SetExceptionHandlerForTest(scoped_handler.Pass()); 56 57 env()->RegisterModule("test", "throw 'hi';"); 58 env()->module_system()->Require("test"); 59 ASSERT_TRUE(handler->handled_exception()); 60 61 ExpectNoAssertionsMade(); 62} 63 64TEST_F(ModuleSystemTest, TestRequire) { 65 ModuleSystem::NativesEnabledScope natives_enabled_scope( 66 env()->module_system()); 67 env()->RegisterModule("add", 68 "exports.Add = function(x, y) { return x + y; };"); 69 env()->RegisterModule("test", 70 "var Add = require('add').Add;" 71 "requireNative('assert').AssertTrue(Add(3, 5) == 8);"); 72 env()->module_system()->Require("test"); 73} 74 75TEST_F(ModuleSystemTest, TestNestedRequire) { 76 ModuleSystem::NativesEnabledScope natives_enabled_scope( 77 env()->module_system()); 78 env()->RegisterModule("add", 79 "exports.Add = function(x, y) { return x + y; };"); 80 env()->RegisterModule("double", 81 "var Add = require('add').Add;" 82 "exports.Double = function(x) { return Add(x, x); };"); 83 env()->RegisterModule("test", 84 "var Double = require('double').Double;" 85 "requireNative('assert').AssertTrue(Double(3) == 6);"); 86 env()->module_system()->Require("test"); 87} 88 89TEST_F(ModuleSystemTest, TestModuleInsulation) { 90 ModuleSystem::NativesEnabledScope natives_enabled_scope( 91 env()->module_system()); 92 env()->RegisterModule("x", 93 "var x = 10;" 94 "exports.X = function() { return x; };"); 95 env()->RegisterModule("y", 96 "var x = 15;" 97 "require('x');" 98 "exports.Y = function() { return x; };"); 99 env()->RegisterModule("test", 100 "var Y = require('y').Y;" 101 "var X = require('x').X;" 102 "var assert = requireNative('assert');" 103 "assert.AssertTrue(!this.hasOwnProperty('x'));" 104 "assert.AssertTrue(Y() == 15);" 105 "assert.AssertTrue(X() == 10);"); 106 env()->module_system()->Require("test"); 107} 108 109TEST_F(ModuleSystemTest, TestNativesAreDisabledOutsideANativesEnabledScope) { 110 env()->RegisterModule("test", 111 "var assert;" 112 "try {" 113 " assert = requireNative('assert');" 114 "} catch (e) {" 115 " var caught = true;" 116 "}" 117 "if (assert) {" 118 " assert.AssertTrue(true);" 119 "}"); 120 env()->module_system()->Require("test"); 121 ExpectNoAssertionsMade(); 122} 123 124TEST_F(ModuleSystemTest, TestNativesAreEnabledWithinANativesEnabledScope) { 125 env()->RegisterModule("test", 126 "var assert = requireNative('assert');" 127 "assert.AssertTrue(true);"); 128 129 { 130 ModuleSystem::NativesEnabledScope natives_enabled(env()->module_system()); 131 { 132 ModuleSystem::NativesEnabledScope natives_enabled_inner( 133 env()->module_system()); 134 } 135 env()->module_system()->Require("test"); 136 } 137} 138 139TEST_F(ModuleSystemTest, TestLazyField) { 140 ModuleSystem::NativesEnabledScope natives_enabled_scope( 141 env()->module_system()); 142 env()->RegisterModule("lazy", "exports.x = 5;"); 143 144 v8::Handle<v8::Object> object = env()->CreateGlobal("object"); 145 146 env()->module_system()->SetLazyField(object, "blah", "lazy", "x"); 147 148 env()->RegisterModule("test", 149 "var assert = requireNative('assert');" 150 "assert.AssertTrue(object.blah == 5);"); 151 env()->module_system()->Require("test"); 152} 153 154TEST_F(ModuleSystemTest, TestLazyFieldYieldingObject) { 155 ModuleSystem::NativesEnabledScope natives_enabled_scope( 156 env()->module_system()); 157 env()->RegisterModule( 158 "lazy", 159 "var object = {};" 160 "object.__defineGetter__('z', function() { return 1; });" 161 "object.x = 5;" 162 "object.y = function() { return 10; };" 163 "exports.object = object;"); 164 165 v8::Handle<v8::Object> object = env()->CreateGlobal("object"); 166 167 env()->module_system()->SetLazyField(object, "thing", "lazy", "object"); 168 169 env()->RegisterModule("test", 170 "var assert = requireNative('assert');" 171 "assert.AssertTrue(object.thing.x == 5);" 172 "assert.AssertTrue(object.thing.y() == 10);" 173 "assert.AssertTrue(object.thing.z == 1);"); 174 env()->module_system()->Require("test"); 175} 176 177TEST_F(ModuleSystemTest, TestLazyFieldIsOnlyEvaledOnce) { 178 ModuleSystem::NativesEnabledScope natives_enabled_scope( 179 env()->module_system()); 180 env()->module_system()->RegisterNativeHandler( 181 "counter", 182 scoped_ptr<NativeHandler>(new CounterNatives(env()->context()))); 183 env()->RegisterModule("lazy", 184 "requireNative('counter').Increment();" 185 "exports.x = 5;"); 186 187 v8::Handle<v8::Object> object = env()->CreateGlobal("object"); 188 189 env()->module_system()->SetLazyField(object, "x", "lazy", "x"); 190 191 env()->RegisterModule("test", 192 "var assert = requireNative('assert');" 193 "var counter = requireNative('counter');" 194 "assert.AssertTrue(counter.Get() == 0);" 195 "object.x;" 196 "assert.AssertTrue(counter.Get() == 1);" 197 "object.x;" 198 "assert.AssertTrue(counter.Get() == 1);"); 199 env()->module_system()->Require("test"); 200} 201 202TEST_F(ModuleSystemTest, TestRequireNativesAfterLazyEvaluation) { 203 ModuleSystem::NativesEnabledScope natives_enabled_scope( 204 env()->module_system()); 205 env()->RegisterModule("lazy", "exports.x = 5;"); 206 v8::Handle<v8::Object> object = env()->CreateGlobal("object"); 207 208 env()->module_system()->SetLazyField(object, "x", "lazy", "x"); 209 env()->RegisterModule("test", 210 "object.x;" 211 "requireNative('assert').AssertTrue(true);"); 212 env()->module_system()->Require("test"); 213} 214 215TEST_F(ModuleSystemTest, TestTransitiveRequire) { 216 ModuleSystem::NativesEnabledScope natives_enabled_scope( 217 env()->module_system()); 218 env()->RegisterModule("dependency", "exports.x = 5;"); 219 env()->RegisterModule("lazy", "exports.output = require('dependency');"); 220 221 v8::Handle<v8::Object> object = env()->CreateGlobal("object"); 222 223 env()->module_system()->SetLazyField(object, "thing", "lazy", "output"); 224 225 env()->RegisterModule("test", 226 "var assert = requireNative('assert');" 227 "assert.AssertTrue(object.thing.x == 5);"); 228 env()->module_system()->Require("test"); 229} 230 231TEST_F(ModuleSystemTest, TestModulesOnlyGetEvaledOnce) { 232 ModuleSystem::NativesEnabledScope natives_enabled_scope( 233 env()->module_system()); 234 env()->module_system()->RegisterNativeHandler( 235 "counter", 236 scoped_ptr<NativeHandler>(new CounterNatives(env()->context()))); 237 238 env()->RegisterModule("incrementsWhenEvaled", 239 "requireNative('counter').Increment();"); 240 env()->RegisterModule("test", 241 "var assert = requireNative('assert');" 242 "var counter = requireNative('counter');" 243 "assert.AssertTrue(counter.Get() == 0);" 244 "require('incrementsWhenEvaled');" 245 "assert.AssertTrue(counter.Get() == 1);" 246 "require('incrementsWhenEvaled');" 247 "assert.AssertTrue(counter.Get() == 1);"); 248 249 env()->module_system()->Require("test"); 250} 251 252TEST_F(ModuleSystemTest, TestOverrideNativeHandler) { 253 ModuleSystem::NativesEnabledScope natives_enabled_scope( 254 env()->module_system()); 255 env()->OverrideNativeHandler("assert", "exports.AssertTrue = function() {};"); 256 env()->RegisterModule("test", "requireNative('assert').AssertTrue(true);"); 257 ExpectNoAssertionsMade(); 258 env()->module_system()->Require("test"); 259} 260 261TEST_F(ModuleSystemTest, TestOverrideNonExistentNativeHandler) { 262 ModuleSystem::NativesEnabledScope natives_enabled_scope( 263 env()->module_system()); 264 env()->OverrideNativeHandler("thing", "exports.x = 5;"); 265 env()->RegisterModule("test", 266 "var assert = requireNative('assert');" 267 "assert.AssertTrue(requireNative('thing').x == 5);"); 268 env()->module_system()->Require("test"); 269} 270 271TEST_F(ModuleSystemTest, TestRequireAsync) { 272 ModuleSystem::NativesEnabledScope natives_enabled_scope( 273 env()->module_system()); 274 env()->RegisterModule("add", 275 "define('add', [], function() {" 276 " return { Add: function(x, y) { return x + y; } };" 277 "});"); 278 env()->RegisterModule("math", 279 "define('math', ['add'], function(add) {" 280 " return { Add: add.Add };" 281 "});"); 282 env()->RegisterModule( 283 "test", 284 "requireAsync('math').then(function(math) {" 285 " requireNative('assert').AssertTrue(math.Add(3, 5) == 8);" 286 "});"); 287 env()->module_system()->Require("test"); 288 RunResolvedPromises(); 289} 290 291TEST_F(ModuleSystemTest, TestRequireAsyncInParallel) { 292 ModuleSystem::NativesEnabledScope natives_enabled_scope( 293 env()->module_system()); 294 env()->RegisterModule("add", 295 "define('add', [], function() {" 296 " return { Add: function(x, y) { return x + y; } };" 297 "});"); 298 env()->RegisterModule( 299 "subtract", 300 "define('subtract', [], function() {" 301 " return { Subtract: function(x, y) { return x - y; } };" 302 "});"); 303 env()->RegisterModule( 304 "math", 305 "exports.AddAndSubtract = function(x, y, z) {" 306 " return Promise.all([requireAsync('add')," 307 " requireAsync('subtract')" 308 " ]).then(function(modules) {" 309 " return modules[1].Subtract(modules[0].Add(x, y), z);" 310 " });" 311 "};"); 312 env()->RegisterModule("test", 313 "var AddAndSubtract = require('math').AddAndSubtract;" 314 "AddAndSubtract(3, 5, 2).then(function(result) {" 315 " requireNative('assert').AssertTrue(result == 6);" 316 "});"); 317 env()->module_system()->Require("test"); 318 RunResolvedPromises(); 319} 320 321TEST_F(ModuleSystemTest, TestNestedRequireAsyncs) { 322 ModuleSystem::NativesEnabledScope natives_enabled_scope( 323 env()->module_system()); 324 env()->RegisterModule("first", 325 "define('first', [], function() {" 326 " return { next: 'second' };" 327 "});"); 328 env()->RegisterModule("second", 329 "define('second', [], function() {" 330 " return { next: '' };" 331 "});"); 332 env()->RegisterModule( 333 "test", 334 "requireAsync('first').then(function(module) {" 335 " return requireAsync(module.next)" 336 "}).then(function(module) {" 337 " requireNative('assert').AssertTrue(module.next === '');" 338 "});"); 339 env()->module_system()->Require("test"); 340 RunResolvedPromises(); 341} 342 343TEST_F(ModuleSystemTest, TestRequireFromAMDModule) { 344 ModuleSystem::NativesEnabledScope natives_enabled_scope( 345 env()->module_system()); 346 env()->RegisterModule("add", 347 "exports.Add = function(x, y) { return x + y; };"); 348 env()->RegisterModule("math", 349 "define('math', [], function() {" 350 " var add = require('add');" 351 " return { Add: add.Add };" 352 "});"); 353 env()->RegisterModule( 354 "test", 355 "requireAsync('math').then(function(math) {" 356 " requireNative('assert').AssertTrue(math.Add(3, 5) == 8);" 357 "});"); 358 env()->module_system()->Require("test"); 359 RunResolvedPromises(); 360} 361 362TEST_F(ModuleSystemTest, TestRequireAsyncFromAMDModule) { 363 ModuleSystem::NativesEnabledScope natives_enabled_scope( 364 env()->module_system()); 365 env()->RegisterModule("add", 366 "define('add', [], function() {" 367 " return { Add: function(x, y) { return x + y; } };" 368 "});"); 369 env()->RegisterModule("math", 370 "define('math', [], function() {" 371 " function Add(x, y) {" 372 " return requireAsync('add').then(function(add) {" 373 " return add.Add(x, y);" 374 " });" 375 " }" 376 " return { Add: Add };" 377 "});"); 378 env()->RegisterModule("test", 379 "requireAsync('math').then(function(math) {" 380 " return math.Add(3, 6);" 381 "}).then(function(result) {" 382 " requireNative('assert').AssertTrue(result == 9);" 383 "});"); 384 env()->module_system()->Require("test"); 385 RunResolvedPromises(); 386} 387 388TEST_F(ModuleSystemTest, TestRequireAsyncFromAnotherContext) { 389 ModuleSystem::NativesEnabledScope natives_enabled_scope( 390 env()->module_system()); 391 env()->RegisterModule( 392 "test", 393 "requireAsync('natives').then(function(natives) {" 394 " natives.requireAsync('ping').then(function(ping) {" 395 " return ping();" 396 " }).then(function(result) {" 397 " requireNative('assert').AssertTrue(result == 'pong');" 398 " });" 399 "});"); 400 scoped_ptr<ModuleSystemTestEnvironment> other_env = CreateEnvironment(); 401 other_env->RegisterModule("ping", 402 "define('ping', ['natives'], function(natives) {" 403 " return function() {" 404 " return 'pong';" 405 " }" 406 "});"); 407 gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule( 408 env()->isolate(), "natives", other_env->module_system()->NewInstance()); 409 gin::ModuleRegistry::From(other_env->context()->v8_context()) 410 ->AddBuiltinModule( 411 env()->isolate(), "natives", env()->module_system()->NewInstance()); 412 env()->module_system()->Require("test"); 413 RunResolvedPromises(); 414} 415 416TEST_F(ModuleSystemTest, TestRequireAsyncBetweenContexts) { 417 ModuleSystem::NativesEnabledScope natives_enabled_scope( 418 env()->module_system()); 419 env()->RegisterModule("pong", 420 "define('pong', [], function() {" 421 " return function() { return 'done'; };" 422 "});"); 423 env()->RegisterModule( 424 "test", 425 "requireAsync('natives').then(function(natives) {" 426 " natives.requireAsync('ping').then(function(ping) {" 427 " return ping();" 428 " }).then(function(pong) {" 429 " return pong();" 430 " }).then(function(result) {" 431 " requireNative('assert').AssertTrue(result == 'done');" 432 " });" 433 "});"); 434 scoped_ptr<ModuleSystemTestEnvironment> other_env = CreateEnvironment(); 435 other_env->RegisterModule("ping", 436 "define('ping', ['natives'], function(natives) {" 437 " return function() {" 438 " return natives.requireAsync('pong');" 439 " }" 440 "});"); 441 gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule( 442 env()->isolate(), "natives", other_env->module_system()->NewInstance()); 443 gin::ModuleRegistry::From(other_env->context()->v8_context()) 444 ->AddBuiltinModule( 445 env()->isolate(), "natives", env()->module_system()->NewInstance()); 446 env()->module_system()->Require("test"); 447 RunResolvedPromises(); 448} 449 450TEST_F(ModuleSystemTest, TestRequireAsyncFromContextWithNoModuleRegistry) { 451 ModuleSystem::NativesEnabledScope natives_enabled_scope( 452 env()->module_system()); 453 env()->RegisterModule("test", 454 "requireAsync('natives').then(function(natives) {" 455 " var AssertTrue = requireNative('assert').AssertTrue;" 456 " natives.requireAsync('foo').then(function() {" 457 " AssertTrue(false);" 458 " }).catch(function(error) {" 459 " AssertTrue(error.message == " 460 " 'Extension view no longer exists');" 461 " });" 462 "});"); 463 scoped_ptr<ModuleSystemTestEnvironment> other_env = CreateEnvironment(); 464 gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule( 465 env()->isolate(), "natives", other_env->module_system()->NewInstance()); 466 other_env->ShutdownGin(); 467 env()->module_system()->Require("test"); 468 RunResolvedPromises(); 469} 470 471TEST_F(ModuleSystemTest, TestRequireAsyncFromContextWithNoModuleSystem) { 472 ModuleSystem::NativesEnabledScope natives_enabled_scope( 473 env()->module_system()); 474 env()->RegisterModule("test", 475 "requireAsync('natives').then(function(natives) {" 476 " requireNative('assert').AssertTrue(" 477 " natives.requireAsync('foo') === undefined);" 478 "});"); 479 scoped_ptr<ModuleSystemTestEnvironment> other_env = CreateEnvironment(); 480 gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule( 481 env()->isolate(), "natives", other_env->module_system()->NewInstance()); 482 other_env->ShutdownModuleSystem(); 483 env()->module_system()->Require("test"); 484 RunResolvedPromises(); 485} 486 487} // namespace extensions 488