OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/navigator_connect/navigator_connect_context_impl.h" | 5 #include "content/browser/navigator_connect/navigator_connect_context_impl.h" |
6 | 6 |
| 7 #include "content/browser/message_port_message_filter.h" |
7 #include "content/browser/message_port_service.h" | 8 #include "content/browser/message_port_service.h" |
8 #include "content/browser/navigator_connect/service_port_service_impl.h" | 9 #include "content/browser/navigator_connect/service_port_service_impl.h" |
9 #include "content/browser/service_worker/service_worker_context_wrapper.h" | 10 #include "content/public/browser/message_port_provider.h" |
10 #include "content/browser/service_worker/service_worker_utils.h" | |
11 #include "content/public/browser/browser_thread.h" | |
12 #include "content/public/browser/navigator_connect_service_factory.h" | 11 #include "content/public/browser/navigator_connect_service_factory.h" |
13 #include "content/public/common/navigator_connect_client.h" | 12 #include "content/public/common/navigator_connect_client.h" |
14 | 13 |
15 namespace content { | 14 namespace content { |
16 | 15 |
17 struct NavigatorConnectContextImpl::Port { | 16 struct NavigatorConnectContextImpl::Port { |
18 // ID of this port. | 17 int message_port_id; |
19 int id; | |
20 // ID of the port this port is connected to. | |
21 int entangled_id; | |
22 | |
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. | |
25 GURL target_url; | |
26 GURL client_origin; | |
27 | |
28 // Set to nullptr when the ServicePortService goes away. | 18 // Set to nullptr when the ServicePortService goes away. |
29 ServicePortServiceImpl* service; | 19 ServicePortServiceImpl* service; |
30 | |
31 // If this port is associated with a service worker, these fields store that | |
32 // information. | |
33 int64 service_worker_registration_id = kInvalidServiceWorkerRegistrationId; | |
34 GURL service_worker_registration_origin; | |
35 }; | 20 }; |
36 | 21 |
37 NavigatorConnectContextImpl::NavigatorConnectContextImpl( | 22 NavigatorConnectContextImpl::NavigatorConnectContextImpl() { |
38 const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context) | 23 } |
39 : service_worker_context_(service_worker_context), next_port_id_(0) {} | |
40 | 24 |
41 NavigatorConnectContextImpl::~NavigatorConnectContextImpl() { | 25 NavigatorConnectContextImpl::~NavigatorConnectContextImpl() { |
42 } | 26 } |
43 | 27 |
44 void NavigatorConnectContextImpl::AddFactory( | 28 void NavigatorConnectContextImpl::AddFactory( |
45 scoped_ptr<NavigatorConnectServiceFactory> factory) { | 29 scoped_ptr<NavigatorConnectServiceFactory> factory) { |
46 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 30 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
47 BrowserThread::PostTask( | 31 BrowserThread::PostTask( |
48 BrowserThread::IO, FROM_HERE, | 32 BrowserThread::IO, FROM_HERE, |
49 base::Bind(&NavigatorConnectContextImpl::AddFactoryOnIOThread, this, | 33 base::Bind(&NavigatorConnectContextImpl::AddFactoryOnIOThread, this, |
50 base::Passed(&factory))); | 34 base::Passed(&factory))); |
51 } | 35 } |
52 | 36 |
53 void NavigatorConnectContextImpl::AddFactoryOnIOThread( | 37 void NavigatorConnectContextImpl::AddFactoryOnIOThread( |
54 scoped_ptr<NavigatorConnectServiceFactory> factory) { | 38 scoped_ptr<NavigatorConnectServiceFactory> factory) { |
55 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 39 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
56 service_factories_.push_back(factory.release()); | 40 service_factories_.push_back(factory.release()); |
57 } | 41 } |
58 | 42 |
59 void NavigatorConnectContextImpl::Connect( | 43 void NavigatorConnectContextImpl::Connect( |
60 const GURL& target_url, | 44 const GURL& target_url, |
61 const GURL& origin, | 45 const GURL& origin, |
62 ServicePortServiceImpl* service_port_service, | 46 ServicePortServiceImpl* service_port_service, |
63 const ConnectCallback& callback) { | 47 const ConnectCallback& callback) { |
64 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 48 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
65 // Create a new message channel. | 49 // Create a new message channel. Use |this| as delegate for both ports until |
66 int client_port_id = next_port_id_++; | 50 // the real delegate for the service port is known later on. |
67 int service_port_id = next_port_id_++; | 51 int client_port_id; |
| 52 int service_port; |
| 53 MessagePortProvider::CreateMessageChannel(this, &client_port_id, |
| 54 &service_port); |
| 55 // Hold messages send to the client while setting up connection. |
| 56 MessagePortService::GetInstance()->HoldMessages(client_port_id); |
68 | 57 |
69 Port& client_port = ports_[client_port_id]; | 58 Port& client_port = ports_[client_port_id]; |
70 client_port.id = client_port_id; | 59 client_port.message_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; | 60 client_port.service = service_port_service; |
75 | 61 |
76 Port& service_port = ports_[service_port_id]; | 62 // The message_port_id stored in the client object is the one associated with |
77 service_port.id = service_port_id; | 63 // the service. |
78 service_port.entangled_id = client_port_id; | 64 NavigatorConnectClient client(target_url, origin, service_port); |
79 service_port.target_url = target_url; | |
80 service_port.client_origin = origin; | |
81 | 65 |
82 // Find the right service worker to service this connection. | 66 // Find factory to handle request, more recently added factories should take |
83 service_worker_context_->FindRegistrationForDocument( | 67 // priority as per comment at NavigatorConnectContext::AddFactory.. |
84 target_url, | 68 NavigatorConnectServiceFactory* factory = nullptr; |
85 base::Bind(&NavigatorConnectContextImpl::GotServiceWorkerRegistration, | 69 for (auto it = service_factories_.rbegin(); it != service_factories_.rend(); |
86 this, callback, client_port_id, service_port_id)); | 70 ++it) { |
87 } | 71 if ((*it)->HandlesUrl(client.target_url)) { |
88 | 72 factory = *it; |
89 void NavigatorConnectContextImpl::PostMessage( | 73 break; |
90 int sender_port_id, | 74 } |
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()); | |
96 | |
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]; | |
100 | |
101 if (port.service_worker_registration_id != | |
102 kInvalidServiceWorkerRegistrationId) { | |
103 // Port is associated with service worker, dispatch message event via | |
104 // ServiceWorkerVersion. | |
105 | |
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); | |
112 | |
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)); | |
118 } | 75 } |
119 | 76 |
120 if (!port.service) { | 77 if (!factory) { |
121 // TODO(mek): Figure out what to do in this situation. | 78 // No factories found. |
122 return; | 79 OnConnectResult(client, client_port_id, callback, nullptr, false); |
123 } | |
124 port.service->PostMessageToClient(port.id, message, sent_message_ports); | |
125 } | |
126 | |
127 void NavigatorConnectContextImpl::GotServiceWorkerRegistration( | |
128 const ConnectCallback& callback, | |
129 int client_port_id, | |
130 int service_port_id, | |
131 ServiceWorkerStatusCode status, | |
132 const scoped_refptr<ServiceWorkerRegistration>& registration) { | |
133 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
134 DCHECK(ports_.find(client_port_id) != ports_.end()); | |
135 DCHECK(ports_.find(service_port_id) != ports_.end()); | |
136 | |
137 if (status != SERVICE_WORKER_OK) { | |
138 // No service worker found, reject connection attempt. | |
139 OnConnectResult(callback, client_port_id, service_port_id, registration, | |
140 status, false, base::string16(), base::string16()); | |
141 return; | 80 return; |
142 } | 81 } |
143 | 82 |
144 ServiceWorkerVersion* active_version = registration->active_version(); | 83 // Actually initiate connection. |
145 if (!active_version) { | 84 factory->Connect( |
146 // No active version, reject connection attempt. | 85 client, base::Bind(&NavigatorConnectContextImpl::OnConnectResult, this, |
147 OnConnectResult(callback, client_port_id, service_port_id, registration, | 86 client, client_port_id, callback)); |
148 status, false, base::string16(), base::string16()); | |
149 return; | |
150 } | |
151 | |
152 Port& service_port = ports_[service_port_id]; | |
153 service_port.service_worker_registration_id = registration->id(); | |
154 service_port.service_worker_registration_origin = | |
155 registration->pattern().GetOrigin(); | |
156 | |
157 active_version->DispatchServicePortConnectEvent( | |
158 base::Bind(&NavigatorConnectContextImpl::OnConnectResult, this, callback, | |
159 client_port_id, service_port_id, registration), | |
160 service_port.target_url, service_port.client_origin, service_port_id); | |
161 } | 87 } |
162 | 88 |
163 void NavigatorConnectContextImpl::ServicePortServiceDestroyed( | 89 void NavigatorConnectContextImpl::ServicePortServiceDestroyed( |
164 ServicePortServiceImpl* service_port_service) { | 90 ServicePortServiceImpl* service_port_service) { |
165 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
166 for (auto& port : ports_) { | 91 for (auto& port : ports_) { |
167 if (port.second.service != service_port_service) | 92 if (port.second.service != service_port_service) |
168 continue; | 93 continue; |
169 port.second.service = nullptr; | 94 port.second.service = nullptr; |
170 // TODO(mek): Should actually inform other side of connections that the | 95 // TODO(mek): Should actually inform other side of connections that the |
171 // connection was closed, or in the case of service workers somehow keep | 96 // connection was closed, or in the case of service workers somehow keep |
172 // track of the connection. | 97 // track of the connection. |
173 } | 98 } |
174 } | 99 } |
175 | 100 |
| 101 void NavigatorConnectContextImpl::SendMessage( |
| 102 int route_id, |
| 103 const MessagePortMessage& message, |
| 104 const std::vector<TransferredMessagePort>& sent_message_ports) { |
| 105 DCHECK(ports_.find(route_id) != ports_.end()); |
| 106 const Port& port = ports_[route_id]; |
| 107 if (!port.service) { |
| 108 // TODO(mek): Figure out what to do in this situation. |
| 109 return; |
| 110 } |
| 111 |
| 112 port.service->PostMessageToClient(route_id, message, sent_message_ports); |
| 113 } |
| 114 |
| 115 void NavigatorConnectContextImpl::SendMessagesAreQueued(int route_id) { |
| 116 NOTREACHED() << "navigator.services endpoints should never queue messages."; |
| 117 } |
| 118 |
176 void NavigatorConnectContextImpl::OnConnectResult( | 119 void NavigatorConnectContextImpl::OnConnectResult( |
| 120 const NavigatorConnectClient& client, |
| 121 int client_message_port_id, |
177 const ConnectCallback& callback, | 122 const ConnectCallback& callback, |
178 int client_port_id, | 123 MessagePortDelegate* delegate, |
179 int service_port_id, | 124 bool data_as_values) { |
180 const scoped_refptr<ServiceWorkerRegistration>& service_worker_registration, | |
181 ServiceWorkerStatusCode status, | |
182 bool accept_connection, | |
183 const base::string16& name, | |
184 const base::string16& data) { | |
185 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 125 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
186 if (accept_connection) { | 126 if (delegate) { |
| 127 DCHECK(!data_as_values) << "Data as values is not currently implemented"; |
187 // TODO(mek): Might have to do something else if the client connection got | 128 // TODO(mek): Might have to do something else if the client connection got |
188 // severed while the service side connection was being set up. | 129 // severed while the service side connection was being set up. |
189 callback.Run(client_port_id, true); | 130 |
| 131 // Update service side port with delegate. |
| 132 MessagePortService::GetInstance()->UpdateMessagePort( |
| 133 client.message_port_id, delegate, client.message_port_id); |
| 134 callback.Run(client_message_port_id, true); |
| 135 MessagePortService::GetInstance()->ReleaseMessages(client_message_port_id); |
190 } else { | 136 } else { |
191 // Destroy ports since connection failed. | 137 // Destroy ports since connection failed. |
192 ports_.erase(service_port_id); | 138 MessagePortService::GetInstance()->Destroy(client.message_port_id); |
193 ports_.erase(client_port_id); | 139 MessagePortService::GetInstance()->Destroy(client_message_port_id); |
| 140 ports_.erase(client_message_port_id); |
194 callback.Run(MSG_ROUTING_NONE, false); | 141 callback.Run(MSG_ROUTING_NONE, false); |
195 } | 142 } |
196 } | 143 } |
197 | 144 |
198 void NavigatorConnectContextImpl::DeliverMessage( | |
199 int port_id, | |
200 const base::string16& message, | |
201 const std::vector<TransferredMessagePort>& sent_message_ports, | |
202 ServiceWorkerStatusCode service_worker_status, | |
203 const scoped_refptr<ServiceWorkerRegistration>& | |
204 service_worker_registration) { | |
205 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
206 DCHECK(ports_.find(port_id) != ports_.end()); | |
207 | |
208 if (service_worker_status != SERVICE_WORKER_OK) { | |
209 // TODO(mek): Do something when no service worker was found. | |
210 return; | |
211 } | |
212 | |
213 ServiceWorkerVersion* active_version = | |
214 service_worker_registration->active_version(); | |
215 if (!active_version) { | |
216 // TODO(mek): Do something when no active version exists. | |
217 return; | |
218 } | |
219 | |
220 const Port& port = ports_[port_id]; | |
221 NavigatorConnectClient client(port.target_url, port.client_origin, port_id); | |
222 active_version->DispatchCrossOriginMessageEvent( | |
223 client, message, sent_message_ports, | |
224 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); | |
225 } | |
226 | |
227 } // namespace content | 145 } // namespace content |
OLD | NEW |