Teach mojo_js_bindings_unittests to load generated JS files

This CL generalizes gin::FileModuleProvider to be able to load modules from
several places in the file system. I've then made use of this facility in
mojo_js_bindings_unittests to load generated JavaScript modules created by
mojom_js_generator.py, similar to how C++ files can #include from the generated
mojom directory.

I've deleted sample_service.js now that we can load it from the generated
directory.

BUG=317398

Review URL: https://codereview.chromium.org/81673002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236711 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/gin/modules/file_module_provider.cc b/gin/modules/file_module_provider.cc
index 0a3e24c6..990dcd00 100644
--- a/gin/modules/file_module_provider.cc
+++ b/gin/modules/file_module_provider.cc
@@ -15,14 +15,15 @@
 namespace {
 
 void AttempToLoadModule(const base::WeakPtr<Runner>& runner,
-                        const base::FilePath& base,
+                        const std::vector<base::FilePath>& search_paths,
                         const std::string& id) {
   if (!runner)
     return;
 
   std::vector<std::string> components;
   base::SplitString(id, '/', &components);
-  base::FilePath path = base;
+
+  base::FilePath path;
   for (size_t i = 0; i < components.size(); ++i) {
     // TODO(abarth): Technically the path components can be UTF-8. We don't
     // handle that case correctly yet.
@@ -30,21 +31,25 @@
   }
   path = path.AddExtension(FILE_PATH_LITERAL("js"));
 
-  std::string source;
-  if (!ReadFileToString(path, &source))
-    return;
+  for (size_t i = 0; i < search_paths.size(); ++i) {
+    std::string source;
+    if (!ReadFileToString(search_paths[i].Append(path), &source))
+      continue;
 
-  Runner::Scope scope(runner.get());
-  v8::Handle<v8::Script> script = v8::Script::New(
-      StringToV8(runner->isolate(), source),
-      StringToV8(runner->isolate(), id));
-  runner->Run(script);
+    Runner::Scope scope(runner.get());
+    v8::Handle<v8::Script> script = v8::Script::New(
+        StringToV8(runner->isolate(), source),
+        StringToV8(runner->isolate(), id));
+    runner->Run(script);
+    return;
+  }
 }
 
 }  // namespace
 
-FileModuleProvider::FileModuleProvider(const base::FilePath& base)
-    : base_(base) {
+FileModuleProvider::FileModuleProvider(
+    const std::vector<base::FilePath>& search_paths)
+    : search_paths_(search_paths) {
 }
 
 FileModuleProvider::~FileModuleProvider() {
@@ -59,8 +64,8 @@
     if (attempted_ids_.count(id))
       continue;
     attempted_ids_.insert(id);
-    base::MessageLoop::current()->PostTask(FROM_HERE,
-        base::Bind(AttempToLoadModule, runner->GetWeakPtr(), base_, id));
+    base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+        AttempToLoadModule, runner->GetWeakPtr(), search_paths_, id));
   }
 }
 
diff --git a/gin/modules/file_module_provider.h b/gin/modules/file_module_provider.h
index 1e6fb88e..e82d8e3 100644
--- a/gin/modules/file_module_provider.h
+++ b/gin/modules/file_module_provider.h
@@ -7,6 +7,7 @@
 
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/files/file_path.h"
 #include "gin/runner.h"
@@ -15,13 +16,14 @@
 
 class FileModuleProvider {
  public:
-  explicit FileModuleProvider(const base::FilePath& base);
+  explicit FileModuleProvider(
+      const std::vector<base::FilePath>& search_paths);
   ~FileModuleProvider();
 
   void AttempToLoadModules(Runner* runner, const std::set<std::string>& ids);
 
  private:
-  base::FilePath base_;
+  std::vector<base::FilePath> search_paths_;
   std::set<std::string> attempted_ids_;
 
   DISALLOW_COPY_AND_ASSIGN(FileModuleProvider);
diff --git a/gin/modules/module_runner_delegate.cc b/gin/modules/module_runner_delegate.cc
index 50b9f5e..3f2422cd 100644
--- a/gin/modules/module_runner_delegate.cc
+++ b/gin/modules/module_runner_delegate.cc
@@ -8,8 +8,9 @@
 
 namespace gin {
 
-ModuleRunnerDelegate::ModuleRunnerDelegate(const base::FilePath& module_base)
-    : module_provider_(module_base) {
+ModuleRunnerDelegate::ModuleRunnerDelegate(
+  const std::vector<base::FilePath>& search_paths)
+    : module_provider_(search_paths) {
 }
 
 ModuleRunnerDelegate::~ModuleRunnerDelegate() {
diff --git a/gin/modules/module_runner_delegate.h b/gin/modules/module_runner_delegate.h
index 4d821d2..a66695f1 100644
--- a/gin/modules/module_runner_delegate.h
+++ b/gin/modules/module_runner_delegate.h
@@ -18,7 +18,8 @@
 
 class ModuleRunnerDelegate : public RunnerDelegate {
  public:
-  explicit ModuleRunnerDelegate(const base::FilePath& module_base);
+  explicit ModuleRunnerDelegate(
+      const std::vector<base::FilePath>& search_paths);
   virtual ~ModuleRunnerDelegate();
 
   void AddBuiltinModule(const std::string& id, ModuleTemplateGetter templ);
diff --git a/gin/shell/gin_main.cc b/gin/shell/gin_main.cc
index e1d95bb..4e18d71 100644
--- a/gin/shell/gin_main.cc
+++ b/gin/shell/gin_main.cc
@@ -31,15 +31,15 @@
   runner->Run(source);
 }
 
-base::FilePath GetModuleBase() {
-  base::FilePath module_base;
-  CHECK(file_util::GetCurrentDirectory(&module_base));
+std::vector<base::FilePath> GetModuleSearchPaths() {
+  std::vector<base::FilePath> module_base(1);
+  CHECK(file_util::GetCurrentDirectory(&module_base[0]));
   return module_base;
 }
 
 class ShellRunnerDelegate : public ModuleRunnerDelegate {
  public:
-  ShellRunnerDelegate() : ModuleRunnerDelegate(GetModuleBase()) {
+  ShellRunnerDelegate() : ModuleRunnerDelegate(GetModuleSearchPaths()) {
     AddBuiltinModule(Console::kModuleName, Console::GetTemplate);
   }
 
diff --git a/gin/test/file_runner.cc b/gin/test/file_runner.cc
index e26d1d1..ff5ac5d 100644
--- a/gin/test/file_runner.cc
+++ b/gin/test/file_runner.cc
@@ -19,16 +19,18 @@
 
 namespace {
 
-base::FilePath GetModuleBase() {
-  base::FilePath path;
-  PathService::Get(base::DIR_SOURCE_ROOT, &path);
-  return path;
+std::vector<base::FilePath> GetModuleSearchPaths() {
+  std::vector<base::FilePath> search_paths(2);
+  PathService::Get(base::DIR_SOURCE_ROOT, &search_paths[0]);
+  PathService::Get(base::DIR_EXE, &search_paths[1]);
+  search_paths[1] = search_paths[1].AppendASCII("gen");
+  return search_paths;
 }
 
 }  // namespace
 
 FileRunnerDelegate::FileRunnerDelegate()
-    : ModuleRunnerDelegate(GetModuleBase()) {
+    : ModuleRunnerDelegate(GetModuleSearchPaths()) {
   AddBuiltinModule(Console::kModuleName, Console::GetTemplate);
   AddBuiltinModule(GTest::kModuleName, GTest::GetTemplate);
 }
diff --git a/mojo/public/bindings/js/codec_unittests.js b/mojo/public/bindings/js/codec_unittests.js
index e177f8c3..5624787 100644
--- a/mojo/public/bindings/js/codec_unittests.js
+++ b/mojo/public/bindings/js/codec_unittests.js
@@ -5,7 +5,7 @@
 define([
     "gtest",
     "mojo/public/bindings/js/codec",
-    "mojo/public/bindings/sample/mojom/sample_service"
+    "mojom/sample_service",
   ], function(gtest, codec, sample) {
   testBar();
   testFoo();
diff --git a/mojo/public/bindings/sample/mojom/sample_service.js b/mojo/public/bindings/sample/mojom/sample_service.js
deleted file mode 100644
index 853b0ca..0000000
--- a/mojo/public/bindings/sample/mojom/sample_service.js
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2013 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.
-
-define([
-    "mojo/public/bindings/js/core",
-    "mojo/public/bindings/js/codec",
-  ], function(core, codec) {
-
-  function Bar() {
-    this.alpha = 0;
-    this.beta = 0;
-    this.gamma = 0;
-  }
-
-  Bar.encodedSize = codec.kStructHeaderSize + 8;
-
-  Bar.decode = function(decoder) {
-    var packed;
-    var val = new Bar();
-    var numberOfBytes = decoder.read32();
-    var numberOfFields = decoder.read32();
-    val.alpha = decoder.read8();
-    val.beta = decoder.read8();
-    val.gamma = decoder.read8();
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    return val;
-  };
-
-  Bar.encode = function(encoder, val) {
-    var packed;
-    encoder.write32(Bar.encodedSize);
-    encoder.write32(3);
-    encoder.write8(val.alpha);
-    encoder.write8(val.beta);
-    encoder.write8(val.gamma);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-  };
-
-  function Foo() {
-    this.x = 0;
-    this.y = 0;
-    this.a = false;
-    this.b = false;
-    this.c = false;
-    this.bar = null;
-    this.data = [];
-    this.extra_bars = [];
-    this.name = "";
-    this.files = [];
-  }
-
-  Foo.encodedSize = codec.kStructHeaderSize + 56;
-
-  Foo.decode = function(decoder) {
-    var packed;
-    var val = new Foo();
-    var numberOfBytes = decoder.read32();
-    var numberOfFields = decoder.read32();
-    val.x = decoder.read32();
-    val.y = decoder.read32();
-    packed = decoder.read8();
-    val.a = (packed >> 0) & 1 ? true : false;
-    val.b = (packed >> 1) & 1 ? true : false;
-    val.c = (packed >> 2) & 1 ? true : false;
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    val.bar = decoder.decodeStructPointer(Bar);
-    val.data = decoder.decodeArrayPointer(codec.Uint8);
-    val.extra_bars = decoder.decodeArrayPointer(new codec.PointerTo(Bar));
-    val.name = decoder.decodeStringPointer();
-    val.files = decoder.decodeArrayPointer(codec.Handle);
-    return val;
-  };
-
-  Foo.encode = function(encoder, val) {
-    var packed;
-    encoder.write32(Foo.encodedSize);
-    encoder.write32(10);
-    encoder.write32(val.x);
-    encoder.write32(val.y);
-    packed = 0;
-    packed |= (val.a & 1) << 0
-    packed |= (val.b & 1) << 1
-    packed |= (val.c & 1) << 2
-    encoder.write8(packed);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.encodeStructPointer(Bar, val.bar);
-    encoder.encodeArrayPointer(codec.Uint8, val.data);
-    encoder.encodeArrayPointer(new codec.PointerTo(Bar), val.extra_bars);
-    encoder.encodeStringPointer(val.name);
-    encoder.encodeArrayPointer(codec.Handle, val.files);
-  };
-
-  function Service_Frobinate_Params() {
-    this.foo = null;
-    this.baz = false;
-    this.port = codec.kInvalidHandle;
-  }
-
-  Service_Frobinate_Params.encodedSize = codec.kStructHeaderSize + 16;
-
-  Service_Frobinate_Params.decode = function(decoder) {
-    var packed;
-    var val = new Service_Frobinate_Params();
-    var numberOfBytes = decoder.read32();
-    var numberOfFields = decoder.read32();
-    val.foo = decoder.decodeStructPointer(Foo);
-    val.baz = decoder.read8() & 1;
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    val.port = decoder.decodeHandle();
-    return val;
-  };
-
-  Service_Frobinate_Params.encode = function(encoder, val) {
-    var packed;
-    encoder.write32(Service_Frobinate_Params.encodedSize);
-    encoder.write32(3);
-    encoder.encodeStructPointer(Foo, val.foo);
-    encoder.write8(1 & val.baz);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.encodeHandle(val.port);
-  };
-
-  function ServiceClient_DidFrobinate_Params() {
-    this.result = 0;
-  }
-
-  ServiceClient_DidFrobinate_Params.encodedSize = codec.kStructHeaderSize + 8;
-
-  ServiceClient_DidFrobinate_Params.decode = function(decoder) {
-    var packed;
-    var val = new ServiceClient_DidFrobinate_Params();
-    var numberOfBytes = decoder.read32();
-    var numberOfFields = decoder.read32();
-    val.result = decoder.read32();
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    decoder.skip(1);
-    return val;
-  };
-
-  ServiceClient_DidFrobinate_Params.encode = function(encoder, val) {
-    var packed;
-    encoder.write32(ServiceClient_DidFrobinate_Params.encodedSize);
-    encoder.write32(1);
-    encoder.write32(val.result);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-    encoder.skip(1);
-  };
-  var kService_Frobinate_Name = 0;
-
-  function ServiceProxy(receiver) {
-    this.receiver_ = receiver;
-  }
-  ServiceProxy.prototype.frobinate = function(foo, baz, port) {
-    var params = new Service_Frobinate_Params();
-    params.foo = foo;
-    params.baz = baz;
-    params.port = port;
-
-    var builder = new codec.MessageBuilder(
-        kService_Frobinate_Name,
-        codec.align(Service_Frobinate_Params.encodedSize));
-    builder.encodeStruct(Service_Frobinate_Params, params);
-    var message = builder.finish();
-    this.receiver_.accept(message);
-  };
-
-  function ServiceStub() {
-  }
-
-  ServiceStub.prototype.accept = function(message) {
-    var reader = new codec.MessageReader(message);
-    switch (reader.messageName) {
-    case kService_Frobinate_Name:
-      var params = reader.decodeStruct(Service_Frobinate_Params);
-      this.frobinate(params.foo, params.baz, params.port);
-      return true;
-    default:
-      return false;
-    }
-  };
-  var kServiceClient_DidFrobinate_Name = 0;
-
-  function ServiceClientProxy(receiver) {
-    this.receiver_ = receiver;
-  }
-  ServiceClientProxy.prototype.didFrobinate = function(result) {
-    var params = new ServiceClient_DidFrobinate_Params();
-    params.result = result;
-
-    var builder = new codec.MessageBuilder(
-        kServiceClient_DidFrobinate_Name,
-        codec.align(ServiceClient_DidFrobinate_Params.encodedSize));
-    builder.encodeStruct(ServiceClient_DidFrobinate_Params, params);
-    var message = builder.finish();
-    this.receiver_.accept(message);
-  };
-
-  function ServiceClientStub() {
-  }
-
-  ServiceClientStub.prototype.accept = function(message) {
-    var reader = new codec.MessageReader(message);
-    switch (reader.messageName) {
-    case kServiceClient_DidFrobinate_Name:
-      var params = reader.decodeStruct(ServiceClient_DidFrobinate_Params);
-      this.didFrobinate(params.result);
-      return true;
-    default:
-      return false;
-    }
-  };
-
-  var exports = {};
-  exports.Bar = Bar;
-  exports.Foo = Foo;
-  exports.ServiceProxy = ServiceProxy;
-  exports.ServiceStub = ServiceStub;
-  exports.ServiceClientProxy = ServiceClientProxy;
-  exports.ServiceClientStub = ServiceClientStub;
-  return exports;
-});
diff --git a/mojo/public/bindings/sample/sample_test.js b/mojo/public/bindings/sample/sample_test.js
index e361b85..66fe158 100644
--- a/mojo/public/bindings/sample/sample_test.js
+++ b/mojo/public/bindings/sample/sample_test.js
@@ -10,7 +10,7 @@
     // be a bug in the module loading system whereby this test doesn't run if
     // we don't import codec here.
     "mojo/public/bindings/js/codec",
-    "mojo/public/bindings/sample/mojom/sample_service"
+    "mojom/sample_service"
   ], function(console, hexdump, gtest, codec, sample) {
 
   var global = this;