mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
177 lines
5.5 KiB
Go
177 lines
5.5 KiB
Go
// Copyright 2015 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
package application
|
|
|
|
import (
|
|
"log"
|
|
"sync"
|
|
|
|
"mojo/public/go/bindings"
|
|
"mojo/public/go/system"
|
|
|
|
"mojo/public/interfaces/application/application"
|
|
sp "mojo/public/interfaces/application/service_provider"
|
|
"mojo/public/interfaces/application/shell"
|
|
)
|
|
|
|
// Delegate is an interface that your mojo application should implement.
|
|
// All methods are called from the same goroutine to make sure that order of
|
|
// calls matches the order of messages sent to underlying message pipe.
|
|
type Delegate interface {
|
|
// Initialize is called exactly once before any other method.
|
|
Initialize(ctx Context)
|
|
|
|
// AcceptConnection is called when another application attempts to open
|
|
// a connection to this application. Close the connection if you no
|
|
// longer need it.
|
|
AcceptConnection(connection *Connection)
|
|
|
|
// Quit is called to request the application shut itself down
|
|
// gracefully.
|
|
Quit()
|
|
}
|
|
|
|
// Context is an interface to information about mojo application environment.
|
|
type Context interface {
|
|
// URL returns the URL the application was found at, after all mappings,
|
|
// resolution, and redirects.
|
|
URL() string
|
|
|
|
// Args returns a list of initial configuration arguments, passed by the
|
|
// Shell.
|
|
Args() []string
|
|
|
|
// ConnectToApplication requests a new connection to an application. You
|
|
// should pass a list of services you want to provide to the requested
|
|
// application.
|
|
ConnectToApplication(remoteURL string, providedServices ...ServiceFactory) *OutgoingConnection
|
|
|
|
// Close closes the main run loop for this application.
|
|
Close()
|
|
}
|
|
|
|
// ApplicationImpl is an utility class for communicating with the Shell, and
|
|
// providing Services to clients.
|
|
type ApplicationImpl struct {
|
|
shell *shell.Shell_Proxy
|
|
args []string
|
|
url string
|
|
// Pointer to the stub that runs this instance of ApplicationImpl.
|
|
runner *bindings.Stub
|
|
quitOnce sync.Once
|
|
|
|
delegate Delegate
|
|
// Protects connections, that can be modified concurrently because of
|
|
// ConnectToApplication calls.
|
|
mu sync.Mutex
|
|
connections []*Connection
|
|
}
|
|
|
|
// Run binds your mojo application to provided message pipe handle and runs it
|
|
// until the application is terminated.
|
|
func Run(delegate Delegate, applicationRequest system.MojoHandle) {
|
|
messagePipe := system.GetCore().AcquireNativeHandle(applicationRequest).ToMessagePipeHandle()
|
|
appRequest := application.Application_Request{bindings.NewMessagePipeHandleOwner(messagePipe)}
|
|
impl := &ApplicationImpl{
|
|
delegate: delegate,
|
|
}
|
|
stub := application.NewApplicationStub(appRequest, impl, bindings.GetAsyncWaiter())
|
|
impl.runner = stub
|
|
for {
|
|
if err := stub.ServeRequest(); err != nil {
|
|
connectionError, ok := err.(*bindings.ConnectionError)
|
|
if !ok || !connectionError.Closed() {
|
|
log.Println(err)
|
|
}
|
|
impl.RequestQuit()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mojo application implementation.
|
|
func (impl *ApplicationImpl) Initialize(shellPointer shell.Shell_Pointer, args *[]string, url string) error {
|
|
impl.shell = shell.NewShellProxy(shellPointer, bindings.GetAsyncWaiter())
|
|
if args != nil {
|
|
impl.args = *args
|
|
}
|
|
impl.url = url
|
|
impl.delegate.Initialize(impl)
|
|
return nil
|
|
}
|
|
|
|
// Mojo application implementation.
|
|
func (impl *ApplicationImpl) AcceptConnection(requestorURL string, services *sp.ServiceProvider_Request, exposedServices *sp.ServiceProvider_Pointer, resolvedURL string) error {
|
|
connection := newConnection(requestorURL, services, exposedServices, resolvedURL)
|
|
impl.delegate.AcceptConnection(connection)
|
|
impl.addConnection(connection)
|
|
return nil
|
|
}
|
|
|
|
// Mojo application implementation.
|
|
func (impl *ApplicationImpl) RequestQuit() error {
|
|
impl.quitOnce.Do(func() {
|
|
impl.delegate.Quit()
|
|
impl.mu.Lock()
|
|
for _, c := range impl.connections {
|
|
c.Close()
|
|
}
|
|
impl.mu.Unlock()
|
|
impl.shell.Close_Proxy()
|
|
impl.runner.Close()
|
|
})
|
|
return nil
|
|
}
|
|
|
|
// Context implementaion.
|
|
func (impl *ApplicationImpl) URL() string {
|
|
return impl.url
|
|
}
|
|
|
|
// Context implementaion.
|
|
func (impl *ApplicationImpl) Args() []string {
|
|
return impl.args
|
|
}
|
|
|
|
// Context implementaion.
|
|
func (impl *ApplicationImpl) ConnectToApplication(remoteURL string, providedServices ...ServiceFactory) *OutgoingConnection {
|
|
servicesRequest, servicesPointer := sp.CreateMessagePipeForServiceProvider()
|
|
exposedServicesRequest, exposedServicesPointer := sp.CreateMessagePipeForServiceProvider()
|
|
if err := impl.shell.ConnectToApplication(remoteURL, &servicesRequest, &exposedServicesPointer); err != nil {
|
|
log.Printf("can't connect to %v: %v", remoteURL, err)
|
|
// In case of error message pipes sent through Shell are closed and
|
|
// the connection will work as if the remote application closed
|
|
// both ServiceProvider's pipes.
|
|
}
|
|
connection := newConnection(impl.url, &exposedServicesRequest, &servicesPointer, remoteURL)
|
|
impl.addConnection(connection)
|
|
return connection.ProvideServices(providedServices...)
|
|
}
|
|
|
|
func (impl *ApplicationImpl) Close() {
|
|
impl.RequestQuit()
|
|
}
|
|
|
|
// addConnection appends connections slice by a provided connection, removing
|
|
// connections that have been closed.
|
|
func (impl *ApplicationImpl) addConnection(c *Connection) {
|
|
impl.mu.Lock()
|
|
i := 0
|
|
for i < len(impl.connections) {
|
|
if impl.connections[i].isClosed {
|
|
last := len(impl.connections) - 1
|
|
impl.connections[i] = impl.connections[last]
|
|
impl.connections[last] = nil
|
|
impl.connections = impl.connections[:last]
|
|
} else {
|
|
i++
|
|
}
|
|
}
|
|
if !c.isClosed {
|
|
impl.connections = append(impl.connections, c)
|
|
}
|
|
impl.mu.Unlock()
|
|
}
|