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::QueuedClientRequest::QueuedClientRequest()
37 : originator(nullptr) {}
39 ApplicationInstance::QueuedClientRequest::~QueuedClientRequest() {
42 ApplicationInstance::ApplicationInstance(
43 ApplicationPtr application
,
44 ApplicationManager
* manager
,
45 const Identity
& originator_identity
,
46 const Identity
& identity
,
47 const CapabilityFilter
& filter
,
48 uint32_t requesting_content_handler_id
,
49 const base::Closure
& on_application_end
)
51 originator_identity_(originator_identity
),
53 filter_(CanonicalizeFilter(filter
)),
54 allow_any_application_(filter
.size() == 1 && filter
.count("*") == 1),
55 requesting_content_handler_id_(requesting_content_handler_id
),
56 on_application_end_(on_application_end
),
57 application_(application
.Pass()),
59 queue_requests_(false) {
60 binding_
.set_connection_error_handler([this]() { OnConnectionError(); });
63 ApplicationInstance::~ApplicationInstance() {
64 STLDeleteElements(&queued_client_requests_
);
67 void ApplicationInstance::InitializeApplication() {
69 binding_
.Bind(GetProxy(&shell
));
70 application_
->Initialize(shell
.Pass(), identity_
.url
.spec());
73 void ApplicationInstance::ConnectToClient(
74 ApplicationInstance
* originator
,
75 const GURL
& requested_url
,
76 const GURL
& requestor_url
,
77 InterfaceRequest
<ServiceProvider
> services
,
78 ServiceProviderPtr exposed_services
,
79 const CapabilityFilter
& filter
,
80 const ConnectToApplicationCallback
& callback
) {
81 callback
.Run(requesting_content_handler_id_
);
82 if (queue_requests_
) {
83 QueuedClientRequest
* queued_request
= new QueuedClientRequest();
84 queued_request
->originator
= originator
;
85 queued_request
->requested_url
= requested_url
;
86 queued_request
->requestor_url
= requestor_url
;
87 queued_request
->services
= services
.Pass();
88 queued_request
->exposed_services
= exposed_services
.Pass();
89 queued_request
->filter
= filter
;
90 queued_client_requests_
.push_back(queued_request
);
94 CallAcceptConnection(originator
, requestor_url
, services
.Pass(),
95 exposed_services
.Pass(), requested_url
);
98 AllowedInterfaces
ApplicationInstance::GetAllowedInterfaces(
99 const Identity
& identity
) const {
100 // Start by looking for interfaces specific to the supplied identity.
101 auto it
= filter_
.find(identity
.url
.spec());
102 if (it
!= filter_
.end())
105 // Fall back to looking for a wildcard rule.
106 it
= filter_
.find("*");
107 if (filter_
.size() == 1 && it
!= filter_
.end())
110 // Finally, nothing is allowed.
111 return AllowedInterfaces();
114 // Shell implementation:
115 void ApplicationInstance::ConnectToApplication(
116 URLRequestPtr app_request
,
117 InterfaceRequest
<ServiceProvider
> services
,
118 ServiceProviderPtr exposed_services
,
119 CapabilityFilterPtr filter
,
120 const ConnectToApplicationCallback
& callback
) {
121 std::string url_string
= app_request
->url
.To
<std::string
>();
122 GURL
url(url_string
);
123 if (!url
.is_valid()) {
124 LOG(ERROR
) << "Error: invalid URL: " << url_string
;
125 callback
.Run(kInvalidContentHandlerID
);
128 if (allow_any_application_
|| filter_
.find(url
.spec()) != filter_
.end()) {
129 CapabilityFilter capability_filter
= GetPermissiveCapabilityFilter();
130 if (!filter
.is_null())
131 capability_filter
= filter
->filter
.To
<CapabilityFilter
>();
132 manager_
->ConnectToApplication(
133 this, app_request
.Pass(), std::string(), identity_
.url
, services
.Pass(),
134 exposed_services
.Pass(), capability_filter
, base::Closure(), callback
);
136 LOG(WARNING
) << "CapabilityFilter prevented connection from: " <<
137 identity_
.url
<< " to: " << url
.spec();
138 callback
.Run(kInvalidContentHandlerID
);
142 void ApplicationInstance::QuitApplication() {
143 queue_requests_
= true;
144 application_
->OnQuitRequested(
145 base::Bind(&ApplicationInstance::OnQuitRequestedResult
,
146 base::Unretained(this)));
149 void ApplicationInstance::CallAcceptConnection(
150 ApplicationInstance
* originator
,
151 const GURL
& requestor_url
,
152 InterfaceRequest
<ServiceProvider
> services
,
153 ServiceProviderPtr exposed_services
,
154 const GURL
& requested_url
) {
155 AllowedInterfaces interfaces
;
156 interfaces
.insert("*");
158 interfaces
= originator
->GetAllowedInterfaces(identity_
);
159 application_
->AcceptConnection(requestor_url
.spec(),
161 exposed_services
.Pass(),
162 Array
<String
>::From(interfaces
).Pass(),
163 requested_url
.spec());
166 void ApplicationInstance::OnConnectionError() {
167 std::vector
<QueuedClientRequest
*> queued_client_requests
;
168 queued_client_requests_
.swap(queued_client_requests
);
169 auto manager
= manager_
;
170 manager_
->OnApplicationInstanceError(this);
173 // If any queued requests came to shell during time it was shutting down,
175 for (auto request
: queued_client_requests
) {
176 mojo::URLRequestPtr
url(mojo::URLRequest::New());
177 url
->url
= mojo::String::From(request
->requested_url
.spec());
178 ApplicationInstance
* originator
=
179 manager
->GetApplicationInstance(originator_identity_
);
180 manager
->ConnectToApplication(
181 originator
, url
.Pass(), std::string(), request
->requestor_url
,
182 request
->services
.Pass(), request
->exposed_services
.Pass(),
183 request
->filter
, base::Closure(), EmptyConnectCallback());
185 STLDeleteElements(&queued_client_requests
);
188 void ApplicationInstance::OnQuitRequestedResult(bool can_quit
) {
192 queue_requests_
= false;
193 for (auto request
: queued_client_requests_
) {
194 CallAcceptConnection(
195 request
->originator
, request
->requestor_url
, request
->services
.Pass(),
196 request
->exposed_services
.Pass(), request
->requested_url
);
198 STLDeleteElements(&queued_client_requests_
);