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.
5 #include "content/browser/navigator_connect/navigator_connect_context_impl.h"
7 #include "content/browser/message_port_service.h"
8 #include "content/browser/navigator_connect/service_port_service_impl.h"
9 #include "content/browser/service_worker/service_worker_context_wrapper.h"
10 #include "content/common/service_worker/service_worker_utils.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/navigator_connect_service_factory.h"
13 #include "content/public/common/navigator_connect_client.h"
17 struct NavigatorConnectContextImpl::Port
{
20 // ID of the port this port is connected to.
23 // Service url and client origin describing this connection. These fields will
24 // always be the same as the same fields for the entangled port.
28 // Set to nullptr when the ServicePortService goes away.
29 ServicePortServiceImpl
* service
= nullptr;
31 // If this port is associated with a service worker, these fields store that
33 int64 service_worker_registration_id
= kInvalidServiceWorkerRegistrationId
;
34 GURL service_worker_registration_origin
;
37 NavigatorConnectContextImpl::NavigatorConnectContextImpl(
38 const scoped_refptr
<ServiceWorkerContextWrapper
>& service_worker_context
)
39 : service_worker_context_(service_worker_context
), next_port_id_(0) {}
41 NavigatorConnectContextImpl::~NavigatorConnectContextImpl() {
44 void NavigatorConnectContextImpl::AddFactory(
45 scoped_ptr
<NavigatorConnectServiceFactory
> factory
) {
46 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
47 BrowserThread::PostTask(
48 BrowserThread::IO
, FROM_HERE
,
49 base::Bind(&NavigatorConnectContextImpl::AddFactoryOnIOThread
, this,
50 base::Passed(&factory
)));
53 void NavigatorConnectContextImpl::AddFactoryOnIOThread(
54 scoped_ptr
<NavigatorConnectServiceFactory
> factory
) {
55 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
56 service_factories_
.push_back(factory
.release());
59 void NavigatorConnectContextImpl::Connect(
60 const GURL
& target_url
,
62 ServicePortServiceImpl
* service_port_service
,
63 const ConnectCallback
& callback
) {
64 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
65 // Create a new message channel.
66 int client_port_id
= next_port_id_
++;
67 int service_port_id
= next_port_id_
++;
69 Port
& client_port
= ports_
[client_port_id
];
70 client_port
.id
= client_port_id
;
71 client_port
.entangled_id
= service_port_id
;
72 client_port
.target_url
= target_url
;
73 client_port
.client_origin
= origin
;
74 client_port
.service
= service_port_service
;
76 Port
& service_port
= ports_
[service_port_id
];
77 service_port
.id
= service_port_id
;
78 service_port
.entangled_id
= client_port_id
;
79 service_port
.target_url
= target_url
;
80 service_port
.client_origin
= origin
;
82 // Find the right service worker to service this connection.
83 service_worker_context_
->FindRegistrationForDocument(
85 base::Bind(&NavigatorConnectContextImpl::GotServiceWorkerRegistration
,
86 this, callback
, client_port_id
, service_port_id
));
89 void NavigatorConnectContextImpl::PostMessage(
91 const MessagePortMessage
& message
,
92 const std::vector
<TransferredMessagePort
>& sent_message_ports
) {
93 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
94 DCHECK(ports_
.find(sender_port_id
) != ports_
.end());
95 DCHECK(message
.message_as_value
.empty());
97 const Port
& sender_port
= ports_
[sender_port_id
];
98 DCHECK(ports_
.find(sender_port
.entangled_id
) != ports_
.end());
99 const Port
& port
= ports_
[sender_port
.entangled_id
];
101 if (port
.service_worker_registration_id
!=
102 kInvalidServiceWorkerRegistrationId
) {
103 // Port is associated with service worker, dispatch message event via
104 // ServiceWorkerVersion.
106 // Hold messages on transferred message ports. Actual delivery of the
107 // message by the service can be asynchronous. When a message is delivered,
108 // WebMessagePortChannelImpl instances will be constructed which send
109 // MessagePortHostMsg_ReleaseMessages to release messages.
110 for (const auto& sent_port
: sent_message_ports
)
111 MessagePortService::GetInstance()->HoldMessages(sent_port
.id
);
113 service_worker_context_
->FindRegistrationForId(
114 port
.service_worker_registration_id
,
115 port
.service_worker_registration_origin
,
116 base::Bind(&NavigatorConnectContextImpl::DeliverMessage
, this, port
.id
,
117 message
.message_as_string
, sent_message_ports
));
122 // TODO(mek): Figure out what to do in this situation.
125 port
.service
->PostMessageToClient(port
.id
, message
, sent_message_ports
);
128 void NavigatorConnectContextImpl::GotServiceWorkerRegistration(
129 const ConnectCallback
& callback
,
132 ServiceWorkerStatusCode status
,
133 const scoped_refptr
<ServiceWorkerRegistration
>& registration
) {
134 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
135 DCHECK(ports_
.find(client_port_id
) != ports_
.end());
136 DCHECK(ports_
.find(service_port_id
) != ports_
.end());
138 if (status
!= SERVICE_WORKER_OK
) {
139 // No service worker found, reject connection attempt.
140 OnConnectResult(callback
, client_port_id
, service_port_id
, registration
,
141 status
, false, base::string16(), base::string16());
145 ServiceWorkerVersion
* active_version
= registration
->active_version();
146 if (!active_version
) {
147 // No active version, reject connection attempt.
148 OnConnectResult(callback
, client_port_id
, service_port_id
, registration
,
149 status
, false, base::string16(), base::string16());
153 Port
& service_port
= ports_
[service_port_id
];
154 service_port
.service_worker_registration_id
= registration
->id();
155 service_port
.service_worker_registration_origin
=
156 registration
->pattern().GetOrigin();
158 active_version
->DispatchServicePortConnectEvent(
159 base::Bind(&NavigatorConnectContextImpl::OnConnectResult
, this, callback
,
160 client_port_id
, service_port_id
, registration
),
161 service_port
.target_url
, service_port
.client_origin
, service_port_id
);
164 void NavigatorConnectContextImpl::ServicePortServiceDestroyed(
165 ServicePortServiceImpl
* service_port_service
) {
166 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
167 for (auto& port
: ports_
) {
168 if (port
.second
.service
!= service_port_service
)
170 port
.second
.service
= nullptr;
171 // TODO(mek): Should actually inform other side of connections that the
172 // connection was closed, or in the case of service workers somehow keep
173 // track of the connection.
177 void NavigatorConnectContextImpl::OnConnectResult(
178 const ConnectCallback
& callback
,
181 const scoped_refptr
<ServiceWorkerRegistration
>& service_worker_registration
,
182 ServiceWorkerStatusCode status
,
183 bool accept_connection
,
184 const base::string16
& name
,
185 const base::string16
& data
) {
186 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
187 if (accept_connection
) {
188 // TODO(mek): Might have to do something else if the client connection got
189 // severed while the service side connection was being set up.
190 callback
.Run(client_port_id
, true);
192 // Destroy ports since connection failed.
193 ports_
.erase(service_port_id
);
194 ports_
.erase(client_port_id
);
195 callback
.Run(MSG_ROUTING_NONE
, false);
199 void NavigatorConnectContextImpl::DeliverMessage(
201 const base::string16
& message
,
202 const std::vector
<TransferredMessagePort
>& sent_message_ports
,
203 ServiceWorkerStatusCode service_worker_status
,
204 const scoped_refptr
<ServiceWorkerRegistration
>&
205 service_worker_registration
) {
206 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
207 DCHECK(ports_
.find(port_id
) != ports_
.end());
209 if (service_worker_status
!= SERVICE_WORKER_OK
) {
210 // TODO(mek): Do something when no service worker was found.
214 ServiceWorkerVersion
* active_version
=
215 service_worker_registration
->active_version();
216 if (!active_version
) {
217 // TODO(mek): Do something when no active version exists.
221 const Port
& port
= ports_
[port_id
];
222 NavigatorConnectClient
client(port
.target_url
, port
.client_origin
, port_id
);
223 active_version
->DispatchCrossOriginMessageEvent(
224 client
, message
, sent_message_ports
,
225 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
228 } // namespace content