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" | |
8 #include "content/browser/message_port_service.h" | 7 #include "content/browser/message_port_service.h" |
9 #include "content/browser/navigator_connect/service_port_service_impl.h" | 8 #include "content/browser/navigator_connect/service_port_service_impl.h" |
10 #include "content/public/browser/message_port_provider.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" |
11 #include "content/public/browser/navigator_connect_service_factory.h" | 12 #include "content/public/browser/navigator_connect_service_factory.h" |
12 #include "content/public/common/navigator_connect_client.h" | 13 #include "content/public/common/navigator_connect_client.h" |
13 | 14 |
14 namespace content { | 15 namespace content { |
15 | 16 |
16 struct NavigatorConnectContextImpl::Port { | 17 struct NavigatorConnectContextImpl::Port { |
17 int message_port_id; | 18 // ID of this port. |
| 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 |
18 // Set to nullptr when the ServicePortService goes away. | 28 // Set to nullptr when the ServicePortService goes away. |
19 ServicePortServiceImpl* service; | 29 ServicePortServiceImpl* service = nullptr; |
| 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; |
20 }; | 35 }; |
21 | 36 |
22 NavigatorConnectContextImpl::NavigatorConnectContextImpl() { | 37 NavigatorConnectContextImpl::NavigatorConnectContextImpl( |
23 } | 38 const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context) |
| 39 : service_worker_context_(service_worker_context), next_port_id_(0) {} |
24 | 40 |
25 NavigatorConnectContextImpl::~NavigatorConnectContextImpl() { | 41 NavigatorConnectContextImpl::~NavigatorConnectContextImpl() { |
26 } | 42 } |
27 | 43 |
28 void NavigatorConnectContextImpl::AddFactory( | 44 void NavigatorConnectContextImpl::AddFactory( |
29 scoped_ptr<NavigatorConnectServiceFactory> factory) { | 45 scoped_ptr<NavigatorConnectServiceFactory> factory) { |
30 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 46 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
31 BrowserThread::PostTask( | 47 BrowserThread::PostTask( |
32 BrowserThread::IO, FROM_HERE, | 48 BrowserThread::IO, FROM_HERE, |
33 base::Bind(&NavigatorConnectContextImpl::AddFactoryOnIOThread, this, | 49 base::Bind(&NavigatorConnectContextImpl::AddFactoryOnIOThread, this, |
34 base::Passed(&factory))); | 50 base::Passed(&factory))); |
35 } | 51 } |
36 | 52 |
37 void NavigatorConnectContextImpl::AddFactoryOnIOThread( | 53 void NavigatorConnectContextImpl::AddFactoryOnIOThread( |
38 scoped_ptr<NavigatorConnectServiceFactory> factory) { | 54 scoped_ptr<NavigatorConnectServiceFactory> factory) { |
39 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 55 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
40 service_factories_.push_back(factory.release()); | 56 service_factories_.push_back(factory.release()); |
41 } | 57 } |
42 | 58 |
43 void NavigatorConnectContextImpl::Connect( | 59 void NavigatorConnectContextImpl::Connect( |
44 const GURL& target_url, | 60 const GURL& target_url, |
45 const GURL& origin, | 61 const GURL& origin, |
46 ServicePortServiceImpl* service_port_service, | 62 ServicePortServiceImpl* service_port_service, |
47 const ConnectCallback& callback) { | 63 const ConnectCallback& callback) { |
48 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 64 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
49 // Create a new message channel. Use |this| as delegate for both ports until | 65 // Create a new message channel. |
50 // the real delegate for the service port is known later on. | 66 int client_port_id = next_port_id_++; |
51 int client_port_id; | 67 int service_port_id = next_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); | |
57 | 68 |
58 Port& client_port = ports_[client_port_id]; | 69 Port& client_port = ports_[client_port_id]; |
59 client_port.message_port_id = 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; |
60 client_port.service = service_port_service; | 74 client_port.service = service_port_service; |
61 | 75 |
62 // The message_port_id stored in the client object is the one associated with | 76 Port& service_port = ports_[service_port_id]; |
63 // the service. | 77 service_port.id = service_port_id; |
64 NavigatorConnectClient client(target_url, origin, service_port); | 78 service_port.entangled_id = client_port_id; |
| 79 service_port.target_url = target_url; |
| 80 service_port.client_origin = origin; |
65 | 81 |
66 // Find factory to handle request, more recently added factories should take | 82 // Find the right service worker to service this connection. |
67 // priority as per comment at NavigatorConnectContext::AddFactory.. | 83 service_worker_context_->FindRegistrationForDocument( |
68 NavigatorConnectServiceFactory* factory = nullptr; | 84 target_url, |
69 for (auto it = service_factories_.rbegin(); it != service_factories_.rend(); | 85 base::Bind(&NavigatorConnectContextImpl::GotServiceWorkerRegistration, |
70 ++it) { | 86 this, callback, client_port_id, service_port_id)); |
71 if ((*it)->HandlesUrl(client.target_url)) { | 87 } |
72 factory = *it; | |
73 break; | |
74 } | |
75 } | |
76 | 88 |
77 if (!factory) { | 89 void NavigatorConnectContextImpl::PostMessage( |
78 // No factories found. | 90 int sender_port_id, |
79 OnConnectResult(client, client_port_id, callback, nullptr, false); | 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)); |
80 return; | 118 return; |
81 } | 119 } |
82 | 120 |
83 // Actually initiate connection. | 121 if (!port.service) { |
84 factory->Connect( | 122 // TODO(mek): Figure out what to do in this situation. |
85 client, base::Bind(&NavigatorConnectContextImpl::OnConnectResult, this, | 123 return; |
86 client, client_port_id, callback)); | 124 } |
| 125 port.service->PostMessageToClient(port.id, message, sent_message_ports); |
| 126 } |
| 127 |
| 128 void NavigatorConnectContextImpl::GotServiceWorkerRegistration( |
| 129 const ConnectCallback& callback, |
| 130 int client_port_id, |
| 131 int service_port_id, |
| 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()); |
| 137 |
| 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()); |
| 142 return; |
| 143 } |
| 144 |
| 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()); |
| 150 return; |
| 151 } |
| 152 |
| 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(); |
| 157 |
| 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); |
87 } | 162 } |
88 | 163 |
89 void NavigatorConnectContextImpl::ServicePortServiceDestroyed( | 164 void NavigatorConnectContextImpl::ServicePortServiceDestroyed( |
90 ServicePortServiceImpl* service_port_service) { | 165 ServicePortServiceImpl* service_port_service) { |
| 166 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
91 for (auto& port : ports_) { | 167 for (auto& port : ports_) { |
92 if (port.second.service != service_port_service) | 168 if (port.second.service != service_port_service) |
93 continue; | 169 continue; |
94 port.second.service = nullptr; | 170 port.second.service = nullptr; |
95 // TODO(mek): Should actually inform other side of connections that the | 171 // TODO(mek): Should actually inform other side of connections that the |
96 // connection was closed, or in the case of service workers somehow keep | 172 // connection was closed, or in the case of service workers somehow keep |
97 // track of the connection. | 173 // track of the connection. |
98 } | 174 } |
99 } | 175 } |
100 | 176 |
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 | |
119 void NavigatorConnectContextImpl::OnConnectResult( | 177 void NavigatorConnectContextImpl::OnConnectResult( |
120 const NavigatorConnectClient& client, | |
121 int client_message_port_id, | |
122 const ConnectCallback& callback, | 178 const ConnectCallback& callback, |
123 MessagePortDelegate* delegate, | 179 int client_port_id, |
124 bool data_as_values) { | 180 int service_port_id, |
| 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) { |
125 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 186 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
126 if (delegate) { | 187 if (accept_connection) { |
127 DCHECK(!data_as_values) << "Data as values is not currently implemented"; | |
128 // TODO(mek): Might have to do something else if the client connection got | 188 // TODO(mek): Might have to do something else if the client connection got |
129 // severed while the service side connection was being set up. | 189 // severed while the service side connection was being set up. |
130 | 190 callback.Run(client_port_id, true); |
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); | |
136 } else { | 191 } else { |
137 // Destroy ports since connection failed. | 192 // Destroy ports since connection failed. |
138 MessagePortService::GetInstance()->Destroy(client.message_port_id); | 193 ports_.erase(service_port_id); |
139 MessagePortService::GetInstance()->Destroy(client_message_port_id); | 194 ports_.erase(client_port_id); |
140 ports_.erase(client_message_port_id); | |
141 callback.Run(MSG_ROUTING_NONE, false); | 195 callback.Run(MSG_ROUTING_NONE, false); |
142 } | 196 } |
143 } | 197 } |
144 | 198 |
| 199 void NavigatorConnectContextImpl::DeliverMessage( |
| 200 int port_id, |
| 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()); |
| 208 |
| 209 if (service_worker_status != SERVICE_WORKER_OK) { |
| 210 // TODO(mek): Do something when no service worker was found. |
| 211 return; |
| 212 } |
| 213 |
| 214 ServiceWorkerVersion* active_version = |
| 215 service_worker_registration->active_version(); |
| 216 if (!active_version) { |
| 217 // TODO(mek): Do something when no active version exists. |
| 218 return; |
| 219 } |
| 220 |
| 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)); |
| 226 } |
| 227 |
145 } // namespace content | 228 } // namespace content |
OLD | NEW |