Skip to content

Commit f207787

Browse files
author
Hans Muller
committed
Defines a JavaScript application that's a client of the shell provided by the JS content handler. Includes a simple hello world example.
BUG= [email protected] Review URL: https://codereview.chromium.org/757703002
1 parent 3374c8e commit f207787

14 files changed

+329
-108
lines changed

examples/js/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
JavaScript Mojo Example Applications
2+
=====================
3+
4+
hello.js, world.js - A minimal application that connects to another.
5+

examples/js/hello.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!mojo:js_content_handler
2+
// Demonstrate one JS Mojo application connecting to another to emit "hello
3+
// world". To run this application with mojo_shell, set DIR to be the absolute
4+
// path for this directory, then:
5+
// mojo_shell "file://$DIR/hello.js file://$DIR/world.js"
6+
// Launches the Mojo hello.js application which connects to the application
7+
// URL specified as a Mojo application argument, world.js in this case.
8+
9+
define("main", [
10+
"console",
11+
"mojo/public/interfaces/application/service_provider.mojom",
12+
], function(console, sp) {
13+
14+
function Application(shell, url) {
15+
this.shell = shell;
16+
console.log(url + ": Hello");
17+
}
18+
19+
Application.prototype.initialize = function(args) {
20+
if (args.length != 2) {
21+
console.log("Expected hello.js URL argument");
22+
return;
23+
}
24+
var serviceProvider = new sp.ServiceProvider.proxyClass();
25+
// TODO(hansmuller): the following step shouldn't be necessary.
26+
var handle = serviceProvider.getConnection$().messagePipeHandle;
27+
this.shell.connectToApplication(args[1], handle);
28+
}
29+
30+
Application.prototype.acceptConnection = function(url, serviceProvider) {
31+
}
32+
33+
return Application;
34+
});

examples/js/main.js

Lines changed: 0 additions & 52 deletions
This file was deleted.

examples/js/world.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!mojo:js_content_handler
2+
// See hello.js.
3+
define("main", ["console"], function(console) {
4+
function Application(shell, url) {
5+
console.log(url + ": World");
6+
}
7+
8+
Application.prototype.initialize = function(args) {
9+
}
10+
11+
Application.prototype.acceptConnection = function(url, serviceProvider) {
12+
}
13+
14+
return Application;
15+
});

gin/modules/module_registry.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,12 @@ void ModuleRegistry::LoadModule(Isolate* isolate,
168168
return;
169169
}
170170
waiting_callbacks_.insert(std::make_pair(id, callback));
171+
172+
for (size_t i = 0; i < pending_modules_.size(); ++i) {
173+
if (pending_modules_[i]->id == id)
174+
return;
175+
}
176+
171177
unsatisfied_dependencies_.insert(id);
172178
}
173179

services/js/BUILD.gn

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ mojo_native_application("js_content_handler") {
1515
"content_handler_main.cc",
1616
"js_app.cc",
1717
"js_app.h",
18+
"js_app_runner_delegate.cc",
19+
"js_app_runner_delegate.h",
20+
"js_app_shell.cc",
21+
"js_app_shell.h",
1822
"mojo_bridge_module.cc",
1923
"mojo_bridge_module.h",
2024
]

services/js/content_handler_main.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
#include "mojo/application/application_runner_chromium.h"
99
#include "mojo/application/content_handler_factory.h"
1010
#include "mojo/public/c/system/main.h"
11+
#include "mojo/public/cpp/application/application_delegate.h"
1112
#include "mojo/public/cpp/application/application_impl.h"
12-
#include "mojo/public/cpp/application/interface_factory_impl.h"
1313
#include "services/js/js_app.h"
1414

1515
namespace mojo {

services/js/js_app.cc

Lines changed: 81 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,60 +5,112 @@
55
#include "services/js/js_app.h"
66

77
#include "base/bind.h"
8-
#include "gin/array_buffer.h"
8+
#include "base/message_loop/message_loop.h"
99
#include "gin/converter.h"
10+
#include "gin/modules/module_registry.h"
11+
#include "gin/try_catch.h"
1012
#include "mojo/common/data_pipe_utils.h"
13+
#include "mojo/edk/js/core.h"
14+
#include "mojo/edk/js/handle.h"
15+
#include "mojo/edk/js/support.h"
16+
#include "mojo/public/cpp/bindings/interface_request.h"
17+
#include "services/js/js_app_shell.h"
1118
#include "services/js/mojo_bridge_module.h"
1219

1320
namespace mojo {
1421
namespace js {
1522

23+
const char JSApp::kMainModuleName[] = "main";
24+
1625
JSApp::JSApp(ShellPtr shell, URLResponsePtr response) : shell_(shell.Pass()) {
17-
// TODO(hansmuller): handle load failure here and below.
26+
isolate_holder_.AddRunMicrotasksObserver();
27+
1828
DCHECK(!response.is_null());
19-
file_name_ = response->url;
20-
bool result = common::BlockingCopyToString(response->body.Pass(), &source_);
21-
DCHECK(result);
29+
std::string url(response->url);
30+
std::string source;
31+
CHECK(common::BlockingCopyToString(response->body.Pass(), &source));
2232

23-
runner_delegate.AddBuiltinModule(MojoInternals::kModuleName,
24-
base::Bind(MojoInternals::GetModule, this));
25-
shell_.set_client(this);
33+
v8::Isolate* isolate = isolate_holder_.isolate();
34+
shell_runner_.reset(new gin::ShellRunner(&runner_delegate_, isolate));
35+
gin::Runner::Scope scope(shell_runner_.get());
36+
shell_runner_->Run(source.c_str(), kMainModuleName);
37+
38+
gin::ModuleRegistry* registry =
39+
gin::ModuleRegistry::From(shell_runner_->GetContextHolder()->context());
40+
registry->LoadModule(
41+
isolate,
42+
kMainModuleName,
43+
base::Bind(&JSApp::OnAppLoaded, base::Unretained(this), url));
2644
}
2745

2846
JSApp::~JSApp() {
47+
app_instance_.Reset();
2948
}
3049

31-
void JSApp::Quit() {
32-
isolate_holder_.RemoveRunMicrotasksObserver();
33-
base::MessageLoop::current()->PostTask(
34-
FROM_HERE, base::Bind(&JSApp::QuitInternal, base::Unretained(this)));
50+
51+
void JSApp::OnAppLoaded(std::string url, v8::Handle<v8::Value> main_module) {
52+
gin::Runner::Scope scope(shell_runner_.get());
53+
gin::TryCatch try_catch;
54+
v8::Isolate* isolate = isolate_holder_.isolate();
55+
56+
v8::Handle<v8::Value> argv[] = {
57+
gin::ConvertToV8(isolate, JSAppShell::Create(isolate, this)),
58+
gin::ConvertToV8(isolate, url)
59+
};
60+
61+
v8::Handle<v8::Function> app_class;
62+
CHECK(gin::ConvertFromV8(isolate, main_module, &app_class));
63+
app_instance_.Reset(isolate, app_class->NewInstance(arraysize(argv), argv));
64+
if (try_catch.HasCaught())
65+
runner_delegate_.UnhandledException(shell_runner_.get(), try_catch);
66+
67+
shell_.set_client(this);
3568
}
3669

37-
MessagePipeHandle JSApp::ConnectToApplication(
38-
const std::string& application_url) {
39-
MessagePipe pipe;
40-
InterfaceRequest<ServiceProvider> request =
41-
MakeRequest<ServiceProvider>(pipe.handle1.Pass());
42-
shell_->ConnectToApplication(application_url, request.Pass());
43-
return pipe.handle0.Pass().release();
70+
void JSApp::ConnectToApplication(const std::string& application_url,
71+
ScopedMessagePipeHandle service_provider) {
72+
shell_->ConnectToApplication(
73+
application_url, MakeRequest<ServiceProvider>(service_provider.Pass()));
4474
}
4575

46-
MessagePipeHandle JSApp::RequestorMessagePipeHandle() {
47-
return requestor_handle_.get();
76+
void JSApp::CallAppInstanceMethod(
77+
const std::string& name, int argc, v8::Handle<v8::Value> argv[]) {
78+
v8::Isolate* isolate = isolate_holder_.isolate();
79+
v8::Local<v8::Object> app =
80+
v8::Local<v8::Object>::New(isolate, app_instance_);
81+
v8::Handle<v8::Value> key = gin::StringToV8(isolate, name);
82+
v8::Handle<v8::Value> value = app->Get(key);
83+
if (!value->IsFunction())
84+
return;
85+
v8::Handle<v8::Function> app_method;
86+
CHECK(gin::ConvertFromV8(isolate, value, &app_method));
87+
shell_runner_->Call(app_method, app, argc, argv);
88+
}
89+
90+
void JSApp::Initialize(Array<String> app_args) {
91+
gin::Runner::Scope scope(shell_runner_.get());
92+
v8::Isolate* isolate = isolate_holder_.isolate();
93+
v8::Handle<v8::Value> argv[] = {
94+
gin::ConvertToV8(isolate, app_args.To<std::vector<std::string>>()),
95+
};
96+
CallAppInstanceMethod("initialize", 1, argv);
4897
}
4998

5099
void JSApp::AcceptConnection(const String& requestor_url,
51100
ServiceProviderPtr provider) {
52-
requestor_handle_ = provider.PassMessagePipe();
53-
54-
isolate_holder_.AddRunMicrotasksObserver();
55-
shell_runner_.reset(
56-
new gin::ShellRunner(&runner_delegate, isolate_holder_.isolate()));
57101
gin::Runner::Scope scope(shell_runner_.get());
58-
shell_runner_->Run(source_.c_str(), file_name_.c_str());
102+
v8::Isolate* isolate = isolate_holder_.isolate();
103+
v8::Handle<v8::Value> argv[] = {
104+
gin::ConvertToV8(isolate, requestor_url.To<std::string>()),
105+
gin::ConvertToV8(isolate, provider.PassMessagePipe().get()),
106+
};
107+
CallAppInstanceMethod("acceptConnection", arraysize(argv), argv);
59108
}
60109

61-
void JSApp::Initialize(Array<String> args) {
110+
void JSApp::Quit() {
111+
isolate_holder_.RemoveRunMicrotasksObserver();
112+
base::MessageLoop::current()->PostTask(
113+
FROM_HERE, base::Bind(&JSApp::QuitInternal, base::Unretained(this)));
62114
}
63115

64116
void JSApp::QuitInternal() {
@@ -68,3 +120,4 @@ void JSApp::QuitInternal() {
68120

69121
} // namespace js
70122
} // namespace mojo
123+

services/js/js_app.h

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,64 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
#ifndef MOJO_SERVICES_JS_JS_APP_H_
6-
#define MOJO_SERVICES_JS_JS_APP_H_
5+
#ifndef SERVICES_JS_JS_APP_H_
6+
#define SERVICES_JS_JS_APP_H_
77

8-
#include "base/memory/ref_counted.h"
9-
#include "base/threading/thread.h"
108
#include "gin/public/isolate_holder.h"
119
#include "gin/shell_runner.h"
1210
#include "mojo/application/content_handler_factory.h"
13-
#include "mojo/edk/js/mojo_runner_delegate.h"
14-
#include "mojo/public/cpp/application/application_delegate.h"
11+
#include "mojo/public/cpp/system/message_pipe.h"
1512
#include "mojo/public/interfaces/application/application.mojom.h"
1613
#include "mojo/public/interfaces/application/shell.mojom.h"
17-
#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
14+
#include "services/js/js_app_runner_delegate.h"
15+
#include "v8/include/v8.h"
1816

1917
namespace mojo {
2018
namespace js {
2119

2220
class JSApp;
2321
class ApplicationDelegateImpl;
2422

25-
// Each JavaScript app started by content handler runs on its own thread and
26-
// in its own V8 isolate. This class represents one running JS app.
23+
// Each JavaScript app started by the content handler runs on its own thread
24+
// and in its own V8 isolate. This class represents one running JS app.
2725

2826
class JSApp : public InterfaceImpl<Application>,
2927
public ContentHandlerFactory::HandledApplicationHolder {
3028
public:
3129
JSApp(ShellPtr shell, URLResponsePtr response);
3230
virtual ~JSApp();
3331

34-
// Called by the JS mojo module to quit this JS app. See mojo.js.
32+
// This method just delegates to shell_->ConnectToApplication().
33+
void ConnectToApplication(const std::string& application_url,
34+
ScopedMessagePipeHandle service_provider);
35+
3536
void Quit();
3637

37-
// Called by the JS mojo module to connect to a Mojo application.
38-
MessagePipeHandle ConnectToApplication(const std::string& application_url);
38+
private:
39+
static const char kMainModuleName[];
3940

40-
// Called by the JS mojo module to retrieve the ServiceProvider message
41-
// pipe handle passed to the JS application's AcceptConnection() method.
42-
MessagePipeHandle RequestorMessagePipeHandle();
41+
void OnAppLoaded(std::string url, v8::Handle<v8::Value> module);
4342

44-
private:
4543
// Application methods:
4644
void AcceptConnection(const String& requestor_url,
4745
ServiceProviderPtr provider) override;
4846
void Initialize(Array<String> args) override;
4947

48+
void CallAppInstanceMethod(
49+
const std::string& name, int argc, v8::Handle<v8::Value> argv[]);
50+
5051
void QuitInternal();
5152

5253
ShellPtr shell_;
53-
js::MojoRunnerDelegate runner_delegate;
54+
JSAppRunnerDelegate runner_delegate_;
5455
gin::IsolateHolder isolate_holder_;
5556
scoped_ptr<gin::ShellRunner> shell_runner_;
56-
std::string source_;
57-
std::string file_name_;
58-
ScopedMessagePipeHandle requestor_handle_;
57+
v8::Persistent<v8::Object> app_instance_;
5958

6059
DISALLOW_COPY_AND_ASSIGN(JSApp);
6160
};
6261

6362
} // namespace js
6463
} // namespace mojo
6564

66-
#endif // MOJO_SERVICES_JS_JS_APP_H_
65+
#endif // SERVICES_JS_JS_APP_H_

0 commit comments

Comments
 (0)