blob: 2ae4f979b43751690100121c6eedd643ea40383d [file] [log] [blame]
// 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 (
sp "mojo/public/interfaces/application/service_provider"
// 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.
// 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.
ConnectToApplication(remoteURL string) *OutgoingConnection
// Close closes the main run loop for this application.
// 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() {
// Mojo application implementation.
func (impl *ApplicationImpl) Initialize(shellPointer shell.Shell_Pointer, args *[]string, url string) error { = shell.NewShellProxy(shellPointer, bindings.GetAsyncWaiter())
if args != nil {
impl.args = *args
impl.url = url
return nil
// Mojo application implementation.
func (impl *ApplicationImpl) AcceptConnection(requestorURL string, resolvedURL string, services sp.ServiceProvider_Request) error {
connection := newConnection(requestorURL, services, resolvedURL)
return nil
// Mojo application implementation.
func (impl *ApplicationImpl) RequestQuit() error {
impl.quitOnce.Do(func() {
for _, c := range impl.connections {
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) *OutgoingConnection {
servicesRequest, servicesPointer := sp.CreateMessagePipeForServiceProvider()
if err :=, servicesRequest); 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.
return &OutgoingConnection{
sp.NewServiceProviderProxy(servicesPointer, bindings.GetAsyncWaiter()),
func (impl *ApplicationImpl) Close() {
// addConnection appends connections slice by a provided connection, removing
// connections that have been closed.
func (impl *ApplicationImpl) addConnection(c *Connection) {
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 {
if !c.isClosed {
impl.connections = append(impl.connections, c)