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