1 // Copyright 2015 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.
5 #include "mojo/shell/application_instance.h"
8 #include "base/stl_util.h"
9 #include "mojo/application/public/interfaces/content_handler.mojom.h"
10 #include "mojo/common/common_type_converters.h"
11 #include "mojo/common/url_type_converters.h"
12 #include "mojo/shell/application_manager.h"
13 #include "mojo/shell/content_handler_connection.h"
19 // It's valid to specify mojo: URLs in the filter either as mojo:foo or
20 // mojo://foo/ - but we store the filter in the latter form.
21 CapabilityFilter
CanonicalizeFilter(const CapabilityFilter
& filter
) {
22 CapabilityFilter canonicalized
;
23 for (CapabilityFilter::const_iterator it
= filter
.begin();
27 canonicalized
[it
->first
] = it
->second
;
29 canonicalized
[GURL(it
->first
).spec()] = it
->second
;
36 ApplicationInstance::ApplicationInstance(
37 ApplicationPtr application
,
38 ApplicationManager
* manager
,
39 const Identity
& originator_identity
,
40 const Identity
& identity
,
41 const CapabilityFilter
& filter
,
42 uint32_t requesting_content_handler_id
,
43 const base::Closure
& on_application_end
)
45 originator_identity_(originator_identity
),
47 filter_(CanonicalizeFilter(filter
)),
48 allow_any_application_(filter
.size() == 1 && filter
.count("*") == 1),
49 requesting_content_handler_id_(requesting_content_handler_id
),
50 on_application_end_(on_application_end
),
51 application_(application
.Pass()),
53 queue_requests_(false) {
54 binding_
.set_connection_error_handler([this]() { OnConnectionError(); });
57 ApplicationInstance::~ApplicationInstance() {
58 for (auto request
: queued_client_requests_
)
59 request
->connect_callback().Run(kInvalidContentHandlerID
);
60 STLDeleteElements(&queued_client_requests_
);
63 void ApplicationInstance::InitializeApplication() {
65 binding_
.Bind(GetProxy(&shell
));
66 application_
->Initialize(shell
.Pass(), identity_
.url
.spec());
69 void ApplicationInstance::ConnectToClient(
70 scoped_ptr
<ConnectToApplicationParams
> params
) {
71 if (queue_requests_
) {
72 queued_client_requests_
.push_back(params
.release());
76 CallAcceptConnection(params
.Pass());
79 // Shell implementation:
80 void ApplicationInstance::ConnectToApplication(
81 URLRequestPtr app_request
,
82 InterfaceRequest
<ServiceProvider
> services
,
83 ServiceProviderPtr exposed_services
,
84 CapabilityFilterPtr filter
,
85 const ConnectToApplicationCallback
& callback
) {
86 std::string url_string
= app_request
->url
.To
<std::string
>();
88 if (!url
.is_valid()) {
89 LOG(ERROR
) << "Error: invalid URL: " << url_string
;
90 callback
.Run(kInvalidContentHandlerID
);
93 if (allow_any_application_
|| filter_
.find(url
.spec()) != filter_
.end()) {
94 CapabilityFilter capability_filter
= GetPermissiveCapabilityFilter();
95 if (!filter
.is_null())
96 capability_filter
= filter
->filter
.To
<CapabilityFilter
>();
97 manager_
->ConnectToApplication(
98 this, app_request
.Pass(), std::string(), services
.Pass(),
99 exposed_services
.Pass(), capability_filter
, base::Closure(), callback
);
101 LOG(WARNING
) << "CapabilityFilter prevented connection from: " <<
102 identity_
.url
<< " to: " << url
.spec();
103 callback
.Run(kInvalidContentHandlerID
);
107 void ApplicationInstance::QuitApplication() {
108 queue_requests_
= true;
109 application_
->OnQuitRequested(
110 base::Bind(&ApplicationInstance::OnQuitRequestedResult
,
111 base::Unretained(this)));
114 void ApplicationInstance::CallAcceptConnection(
115 scoped_ptr
<ConnectToApplicationParams
> params
) {
116 params
->connect_callback().Run(requesting_content_handler_id_
);
117 AllowedInterfaces interfaces
;
118 interfaces
.insert("*");
119 if (!params
->originator_identity().is_null())
120 interfaces
= GetAllowedInterfaces(params
->originator_filter(), identity_
);
122 application_
->AcceptConnection(
123 params
->originator_identity().url
.spec(), params
->TakeServices(),
124 params
->TakeExposedServices(), Array
<String
>::From(interfaces
).Pass(),
125 params
->app_url().spec());
128 void ApplicationInstance::OnConnectionError() {
129 std::vector
<ConnectToApplicationParams
*> queued_client_requests
;
130 queued_client_requests_
.swap(queued_client_requests
);
131 auto manager
= manager_
;
132 manager_
->OnApplicationInstanceError(this);
135 // If any queued requests came to shell during time it was shutting down,
137 for (auto request
: queued_client_requests
) {
138 // Unfortunately, it is possible that |request->app_url_request()| is null
139 // at this point. Consider the following sequence:
140 // 1) connect_request_1 arrives at the application manager; the manager
141 // decides to fetch the app.
142 // 2) connect_request_2 arrives for the same app; because the app is not
143 // running yet, the manager decides to fetch the app again.
144 // 3) The fetch for step (1) completes and an application instance app_a is
146 // 4) app_a goes into two-phase shutdown.
147 // 5) The fetch for step (2) completes; the manager finds that there is a
148 // running app already, so it connects to app_a.
149 // 6) connect_request_2 is queued (and eventually gets here), but its
150 // original_request field was already lost to NetworkFetcher at step (2).
152 // TODO(yzshen): It seems we should register a pending application instance
153 // before starting the fetch. So at step (2) the application manager knows
154 // that it can wait for the first fetch to complete instead of doing a
155 // second one directly.
156 if (!request
->app_url_request()) {
157 URLRequestPtr url_request
= mojo::URLRequest::New();
158 url_request
->url
= request
->app_url().spec();
159 request
->SetURLInfo(url_request
.Pass());
161 manager
->ConnectToApplication(make_scoped_ptr(request
));
165 void ApplicationInstance::OnQuitRequestedResult(bool can_quit
) {
169 queue_requests_
= false;
170 for (auto request
: queued_client_requests_
)
171 CallAcceptConnection(make_scoped_ptr(request
));
173 queued_client_requests_
.clear();