| 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 |