mozart: Add a simple tiling view manager.

The tile view can be used to launch several applications based on the
Mozart view system side by side all contained within the same view.  It
is a simple demonstration of view composition.

Exempt all example code from logspam presubmit checks because it's
often quite useful to be able to illustrate the operation of an
example program to an interactive user.

R=abarth@google.com

Review URL: https://codereview.chromium.org/1425543002 .
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 34eb017..769b783 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -641,7 +641,7 @@
                 input_api.DEFAULT_BLACK_LIST +
                 (r"^base/logging\.h$",
                  r"^base/logging\.cc$",
-                 r"^examples/wget/wget\.cc$",
+                 r"^examples/.*$",
                  r"^mojo/nacl/sfi/nacl_bindings/mojo_syscall\.cc$",
                  r"^shell/application_manager/network_fetcher\.cc$",
                  r"^shell/tracer\.cc$",
diff --git a/examples/BUILD.gn b/examples/BUILD.gn
index b4fe608..455eb7a 100644
--- a/examples/BUILD.gn
+++ b/examples/BUILD.gn
@@ -48,6 +48,7 @@
   if (is_linux || is_android) {
     deps += [
       "//examples/ui/spinning_cube",
+      "//examples/ui/tile",
       "//examples/surfaces_app",
     ]
   }
diff --git a/examples/ui/tile/BUILD.gn b/examples/ui/tile/BUILD.gn
new file mode 100644
index 0000000..1727e09
--- /dev/null
+++ b/examples/ui/tile/BUILD.gn
@@ -0,0 +1,36 @@
+# 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.
+
+import("//mojo/public/mojo_application.gni")
+
+mojo_native_application("tile") {
+  output_name = "tile_view"
+
+  sources = [
+    "main.cc",
+    "tile_app.cc",
+    "tile_app.h",
+    "tile_view.cc",
+    "tile_view.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/gpu",
+    "//mojo/public/c/gpu",
+    "//mojo/public/c/gpu:gpu_onscreen",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/environment",
+    "//mojo/public/cpp/system",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/input_events/interfaces",
+    "//mojo/services/surfaces/cpp",
+    "//mojo/services/surfaces/interfaces",
+    "//mojo/services/ui/views/interfaces",
+    "//url",
+  ]
+}
diff --git a/examples/ui/tile/README.md b/examples/ui/tile/README.md
new file mode 100644
index 0000000..a6afbfa
--- /dev/null
+++ b/examples/ui/tile/README.md
@@ -0,0 +1,15 @@
+# Mozart View Tiling Example
+
+This directory contains a simple application which embeds any number of
+views from other applications all tiled in a row.
+
+The applications must implement the ViewProvider interface and register
+their Views with the ViewManager for this to work.
+
+## USAGE
+
+  out/Debug/mojo_shell "mojo:launcher mojo:tile_view?<app1>[,<app2>[,...]]"
+
+Specify the urls of the views to embed as a comma-delimited query parameter.
+
+  eg. out/Debug/mojo_shell "mojo:launcher mojo:tile_view?mojo:spinning_cube,mojo:moterm_example_app"
diff --git a/examples/ui/tile/main.cc b/examples/ui/tile/main.cc
new file mode 100644
index 0000000..930aca3
--- /dev/null
+++ b/examples/ui/tile/main.cc
@@ -0,0 +1,12 @@
+// 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.
+
+#include "examples/ui/tile/tile_app.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(new examples::TileApp);
+  return runner.Run(application_request);
+}
diff --git a/examples/ui/tile/tile_app.cc b/examples/ui/tile/tile_app.cc
new file mode 100644
index 0000000..3360e1c
--- /dev/null
+++ b/examples/ui/tile/tile_app.cc
@@ -0,0 +1,65 @@
+// 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.
+
+#include <vector>
+
+#include "base/strings/string_split.h"
+#include "examples/ui/tile/tile_app.h"
+#include "examples/ui/tile/tile_view.h"
+#include "url/gurl.h"
+
+namespace examples {
+
+class TileViewProvider : public mojo::ui::ViewProvider {
+ public:
+  TileViewProvider(mojo::ApplicationImpl* app_impl,
+                   const std::vector<std::string>& view_urls)
+      : app_impl_(app_impl), view_urls_(view_urls) {}
+  ~TileViewProvider() override {}
+
+ private:
+  // |ViewProvider|:
+  void CreateView(mojo::InterfaceRequest<mojo::ServiceProvider> services,
+                  mojo::ServiceProviderPtr exposed_services,
+                  const CreateViewCallback& callback) override {
+    new TileView(app_impl_, view_urls_, callback);
+  }
+
+  mojo::ApplicationImpl* app_impl_;
+  std::vector<std::string> view_urls_;
+
+  DISALLOW_COPY_AND_ASSIGN(TileViewProvider);
+};
+
+TileApp::TileApp() {}
+
+TileApp::~TileApp() {}
+
+void TileApp::Initialize(mojo::ApplicationImpl* app_impl) {
+  app_impl_ = app_impl;
+}
+
+bool TileApp::ConfigureIncomingConnection(
+    mojo::ApplicationConnection* connection) {
+  connection->AddService<mojo::ui::ViewProvider>(this);
+  return true;
+}
+
+void TileApp::Create(mojo::ApplicationConnection* connection,
+                     mojo::InterfaceRequest<mojo::ui::ViewProvider> request) {
+  GURL url(connection->GetConnectionURL());
+  std::vector<std::string> view_urls;
+  base::SplitString(url.query(), ',', &view_urls);
+
+  if (view_urls.empty()) {
+    LOG(ERROR) << "Must supply comma-delimited URLs of mojo views to tile as a "
+                  "query parameter.";
+    return;
+  }
+
+  bindings_.AddBinding(new TileViewProvider(app_impl_, view_urls),
+                       request.Pass());
+}
+
+}  // namespace examples
diff --git a/examples/ui/tile/tile_app.h b/examples/ui/tile/tile_app.h
new file mode 100644
index 0000000..35227db
--- /dev/null
+++ b/examples/ui/tile/tile_app.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef EXAMPLES_UI_TILE_TILE_APP_H_
+#define EXAMPLES_UI_TILE_TILE_APP_H_
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/common/strong_binding_set.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
+
+namespace examples {
+
+class TileApp : public mojo::ApplicationDelegate,
+                public mojo::InterfaceFactory<mojo::ui::ViewProvider> {
+ public:
+  TileApp();
+  ~TileApp() override;
+
+  // |ApplicationDelegate|:
+  void Initialize(mojo::ApplicationImpl* app) override;
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+
+  // |InterfaceFactory<mojo::ui::ViewProvider>|:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::ui::ViewProvider> request) override;
+
+ private:
+  mojo::ApplicationImpl* app_impl_;
+  mojo::StrongBindingSet<mojo::ui::ViewProvider> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(TileApp);
+};
+
+}  // namespace examples
+
+#endif  // EXAMPLES_UI_TILE_TILE_APP_H_
diff --git a/examples/ui/tile/tile_view.cc b/examples/ui/tile/tile_view.cc
new file mode 100644
index 0000000..34c984e
--- /dev/null
+++ b/examples/ui/tile/tile_view.cc
@@ -0,0 +1,283 @@
+// 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.
+
+#include "examples/ui/tile/tile_view.h"
+#include "mojo/services/surfaces/cpp/surfaces_utils.h"
+#include "mojo/services/surfaces/interfaces/quads.mojom.h"
+
+namespace examples {
+
+TileView::TileView(mojo::ApplicationImpl* app_impl,
+                   const std::vector<std::string>& view_urls,
+                   const mojo::ui::ViewProvider::CreateViewCallback& callback)
+    : app_impl_(app_impl),
+      view_urls_(view_urls),
+      callback_(callback),
+      binding_(this),
+      surface_id_namespace_(0),
+      pending_child_layout_count_(0),
+      frame_pending_(false),
+      weak_ptr_factory_(this) {
+  app_impl_->ConnectToService("mojo:surfaces_service", &surfaces_);
+  app_impl_->ConnectToService("mojo:view_manager_service", &view_manager_);
+
+  surfaces_->GetIdNamespace(base::Bind(&TileView::OnSurfaceIdNamespaceAvailable,
+                                       base::Unretained(this)));
+}
+
+TileView::~TileView() {}
+
+void TileView::OnSurfaceIdNamespaceAvailable(uint32_t id_namespace) {
+  surface_id_namespace_ = id_namespace;
+  InitView();
+}
+
+void TileView::InitView() {
+  // Register the view.
+  mojo::ui::ViewPtr view;
+  binding_.Bind(mojo::GetProxy(&view));
+  view_manager_->RegisterView(view.Pass(), mojo::GetProxy(&view_host_),
+                              callback_);
+
+  // Connect to all child views.
+  uint32_t child_key = 0;
+  for (const auto& url : view_urls_) {
+    // Start connecting to the view provider.
+    mojo::ui::ViewProviderPtr provider;
+    app_impl_->ConnectToService(url, &provider);
+    provider.set_connection_error_handler(
+        base::Bind(&TileView::OnChildConnectionError, base::Unretained(this),
+                   child_key, url));
+
+    // Create the view.
+    // We include the provider reference in the callback so that the
+    // binding will be kept alive until the callback completes.
+    LOG(INFO) << "Connecting to view: child_key=" << child_key
+              << ", url=" << url;
+    provider->CreateView(
+        nullptr, nullptr,
+        base::Bind(&TileView::OnChildCreated, base::Unretained(this), child_key,
+                   url, base::Passed(provider.Pass())));
+    child_key++;
+  }
+}
+
+void TileView::OnChildConnectionError(uint32_t child_key,
+                                      const std::string& url) {
+  LOG(ERROR) << "Could not connect to view: child_key=" << child_key
+             << ", url=" << url;
+}
+
+void TileView::OnChildCreated(uint32_t child_key,
+                              const std::string& url,
+                              mojo::ui::ViewProviderPtr provider,
+                              mojo::ui::ViewTokenPtr token) {
+  DCHECK(views_.find(child_key) == views_.end());
+  LOG(INFO) << "View created: child_key=" << child_key << ", url=" << url;
+
+  view_host_->AddChild(child_key, token.Pass());
+  views_.emplace(
+      std::make_pair(child_key, std::unique_ptr<ViewData>(new ViewData(url))));
+
+  // Note that the view provider will be destroyed once this function
+  // returns which is fine now that we are done creating the view.
+}
+
+void TileView::OnChildUnavailable(uint32_t child_key,
+                                  const OnChildUnavailableCallback& callback) {
+  auto it = views_.find(child_key);
+  DCHECK(it != views_.end());
+  LOG(ERROR) << "View died unexpectedly: child_key=" << child_key
+             << ", url=" << it->second->url;
+
+  std::unique_ptr<ViewData> view_data = std::move(it->second);
+  views_.erase(it);
+
+  view_host_->RemoveChild(child_key);
+
+  if (view_data->layout_pending) {
+    DCHECK(pending_child_layout_count_);
+    pending_child_layout_count_--;
+    FinishLayout();
+  }
+
+  callback.Run();
+}
+
+void TileView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
+                        mojo::Array<uint32_t> children_needing_layout,
+                        const OnLayoutCallback& callback) {
+  // Create a new surface the first time or if the size has changed.
+  mojo::Size new_size;
+  new_size.width = layout_params->constraints->max_width;
+  new_size.height = layout_params->constraints->max_height;
+  if (!surface_id_ || !size_.Equals(new_size)) {
+    if (!surface_id_) {
+      surface_id_ = mojo::SurfaceId::New();
+      surface_id_->id_namespace = surface_id_namespace_;
+    } else {
+      surfaces_->DestroySurface(surface_id_->local);
+    }
+    surface_id_->local++;
+    size_ = new_size;
+    surfaces_->CreateSurface(surface_id_->local);
+  }
+
+  // Wipe out cached layout information for children needing layout.
+  for (uint32_t child_key : children_needing_layout) {
+    auto view_it = views_.find(child_key);
+    if (view_it != views_.end())
+      view_it->second->layout_info.reset();
+  }
+
+  // Layout all children in a row.
+  if (!views_.empty()) {
+    uint32_t index = 0;
+    uint32_t base_width = new_size.width / views_.size();
+    uint32_t excess_width = new_size.width % views_.size();
+    uint32_t x = 0;
+    for (auto it = views_.begin(); it != views_.end(); ++it, ++index) {
+      ViewData* view_data = it->second.get();
+      DCHECK(!view_data->layout_pending);
+
+      // Distribute any excess width among the leading children.
+      uint32_t child_width = base_width;
+      if (excess_width) {
+        child_width++;
+        excess_width--;
+      }
+      uint32_t child_height = new_size.height;
+      uint32_t child_x = x;
+      x += child_width;
+
+      view_data->layout_bounds.x = child_x;
+      view_data->layout_bounds.y = 0;
+      view_data->layout_bounds.width = child_width;
+      view_data->layout_bounds.height = child_height;
+
+      mojo::ui::ViewLayoutParamsPtr params = mojo::ui::ViewLayoutParams::New();
+      params->constraints = mojo::ui::BoxConstraints::New();
+      params->constraints->min_width = child_width;
+      params->constraints->max_width = child_width;
+      params->constraints->min_height = child_height;
+      params->constraints->max_height = child_height;
+      params->device_pixel_ratio = layout_params->device_pixel_ratio;
+
+      if (view_data->layout_info && view_data->layout_params.Equals(params))
+        continue;  // no layout work to do
+
+      pending_child_layout_count_++;
+      view_data->layout_pending = true;
+      view_data->layout_params = params.Clone();
+      view_data->layout_info.reset();
+
+      view_host_->LayoutChild(it->first, params.Pass(),
+                              base::Bind(&TileView::OnChildLayoutFinished,
+                                         base::Unretained(this), it->first));
+    }
+  }
+
+  // Store the callback until layout of all children is finished.
+  pending_layout_callback_ = callback;
+  FinishLayout();
+}
+
+void TileView::OnChildLayoutFinished(
+    uint32_t child_key,
+    mojo::ui::ViewLayoutInfoPtr child_layout_info) {
+  auto it = views_.find(child_key);
+  if (it != views_.end()) {
+    ViewData* view_data = it->second.get();
+    DCHECK(view_data->layout_pending);
+    DCHECK(pending_child_layout_count_);
+    pending_child_layout_count_--;
+    view_data->layout_pending = false;
+    view_data->layout_info = child_layout_info.Pass();
+    FinishLayout();
+  }
+}
+
+void TileView::FinishLayout() {
+  if (frame_pending_ || pending_layout_callback_.is_null())
+    return;
+
+  // Wait until all children have laid out.
+  // TODO(jeffbrown): There should be a timeout on this.
+  if (pending_child_layout_count_)
+    return;
+
+  // Produce a new frame.
+  mojo::FramePtr frame = mojo::Frame::New();
+  frame->resources.resize(0u);
+
+  mojo::Rect bounds;
+  bounds.width = size_.width;
+  bounds.height = size_.height;
+  mojo::PassPtr pass = mojo::CreateDefaultPass(1, bounds);
+  pass->shared_quad_states.resize(0u);
+  pass->quads.resize(0u);
+
+  for (auto it = views_.cbegin(); it != views_.cend(); it++) {
+    const ViewData& view_data = *(it->second.get());
+
+    mojo::QuadPtr quad = mojo::Quad::New();
+    quad->rect = view_data.layout_bounds.Clone();
+    quad->rect->x = 0;
+    quad->rect->y = 0;
+    quad->opaque_rect = quad->rect.Clone();
+    quad->visible_rect = quad->rect.Clone();
+    quad->shared_quad_state_index = pass->shared_quad_states.size();
+
+    mojo::Size size;
+    size.width = view_data.layout_bounds.width;
+    size.height = view_data.layout_bounds.height;
+
+    mojo::SharedQuadStatePtr quad_state = mojo::CreateDefaultSQS(size);
+    quad_state->content_to_target_transform->matrix[3] =
+        view_data.layout_bounds.x;
+    pass->shared_quad_states.push_back(quad_state.Pass());
+
+    if (it->second->layout_info) {
+      quad->material = mojo::Material::SURFACE_CONTENT;
+      quad->surface_quad_state = mojo::SurfaceQuadState::New();
+      quad->surface_quad_state->surface =
+          view_data.layout_info->surface_id.Clone();
+    } else {
+      quad->material = mojo::Material::SOLID_COLOR;
+      quad->solid_color_quad_state = mojo::SolidColorQuadState::New();
+      quad->solid_color_quad_state->color = mojo::Color::New();
+      quad->solid_color_quad_state->color->rgba = 0xffff00ff;
+    }
+
+    pass->quads.push_back(quad.Pass());
+  }
+
+  frame->passes.push_back(pass.Pass());
+
+  frame_pending_ = true;
+  surfaces_->SubmitFrame(
+      surface_id_->local, frame.Pass(),
+      base::Bind(&TileView::OnFrameSubmitted, base::Unretained(this)));
+
+  // Submit the new layout information.
+  mojo::ui::ViewLayoutInfoPtr info = mojo::ui::ViewLayoutInfo::New();
+  info->size = size_.Clone();
+  info->surface_id = surface_id_->Clone();
+  pending_layout_callback_.Run(info.Pass());
+  pending_layout_callback_.reset();
+}
+
+void TileView::OnFrameSubmitted() {
+  DCHECK(frame_pending_);
+
+  frame_pending_ = false;
+  FinishLayout();
+}
+
+TileView::ViewData::ViewData(const std::string& url)
+    : url(url), layout_pending(false) {}
+
+TileView::ViewData::~ViewData() {}
+
+}  // namespace examples
diff --git a/examples/ui/tile/tile_view.h b/examples/ui/tile/tile_view.h
new file mode 100644
index 0000000..a0132cf
--- /dev/null
+++ b/examples/ui/tile/tile_view.h
@@ -0,0 +1,94 @@
+// 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.
+
+#ifndef EXAMPLES_UI_TILE_TILE_VIEW_H_
+#define EXAMPLES_UI_TILE_TILE_VIEW_H_
+
+#include <map>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/geometry/interfaces/geometry.mojom.h"
+#include "mojo/services/surfaces/interfaces/surfaces.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
+#include "mojo/services/ui/views/interfaces/views.mojom.h"
+
+namespace examples {
+
+class TileView : public mojo::ui::View {
+ public:
+  TileView(mojo::ApplicationImpl* app_impl_,
+           const std::vector<std::string>& view_urls,
+           const mojo::ui::ViewProvider::CreateViewCallback& callback);
+
+  ~TileView() override;
+
+ private:
+  struct ViewData {
+    explicit ViewData(const std::string& url);
+    ~ViewData();
+
+    const std::string url;
+
+    bool layout_pending;
+    mojo::ui::ViewLayoutParamsPtr layout_params;
+    mojo::ui::ViewLayoutInfoPtr layout_info;
+    mojo::Rect layout_bounds;
+  };
+
+  // |View|:
+  void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
+                mojo::Array<uint32_t> children_needing_layout,
+                const OnLayoutCallback& callback) override;
+  void OnChildUnavailable(uint32_t child_key,
+                          const OnChildUnavailableCallback& callback) override;
+
+  void OnSurfaceIdNamespaceAvailable(uint32_t id_namespace);
+
+  void InitView();
+  void OnChildConnectionError(uint32_t child_key, const std::string& url);
+  void OnChildCreated(uint32_t child_key,
+                      const std::string& url,
+                      mojo::ui::ViewProviderPtr provider,
+                      mojo::ui::ViewTokenPtr token);
+  void OnChildLayoutFinished(uint32_t child_key,
+                             mojo::ui::ViewLayoutInfoPtr child_layout_info);
+  void FinishLayout();
+
+  void OnFrameSubmitted();
+
+  mojo::ApplicationImpl* app_impl_;
+  std::vector<std::string> view_urls_;
+  mojo::ui::ViewProvider::CreateViewCallback callback_;
+  mojo::StrongBinding<mojo::ui::View> binding_;
+
+  mojo::SurfacePtr surfaces_;
+  mojo::SurfaceIdPtr surface_id_;
+  uint32_t surface_id_namespace_;
+
+  mojo::ui::ViewManagerPtr view_manager_;
+  mojo::ui::ViewHostPtr view_host_;
+
+  std::map<uint32_t, std::unique_ptr<ViewData>> views_;
+
+  mojo::Size size_;
+  OnLayoutCallback pending_layout_callback_;
+  uint32_t pending_child_layout_count_;
+  bool frame_pending_;
+
+  base::WeakPtrFactory<TileView> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(TileView);
+};
+
+}  // namespace examples
+
+#endif  // EXAMPLES_UI_TILE_TILE_VIEW_H_