[go: nahoru, domu]

Moves TransientWindowClient to ui/aura/client and adds observer

Mus is going to need to use this from within aura, so I'm moving it to
ui/aura/client. Additionally I'm moving creation of WmState to
AuraTestHelper. Again, mus is going to need it aura test code already
depends upon ui/wm, so I can't think of a reason not to do it
everywhere.

BUG=659155
TEST=covered by tests
R=ben@chromium.org

Review-Url: https://codereview.chromium.org/2453953003
Cr-Commit-Position: refs/heads/master@{#428087}
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index d3377a6..f882d7c 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -20,6 +20,7 @@
 #include "ash/test/shell_test_api.h"
 #include "ash/test/test_screenshot_delegate.h"
 #include "ash/test/test_shell_delegate.h"
+#include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "ui/aura/env.h"
 #include "ui/aura/input_state_lookup.h"
@@ -34,6 +35,7 @@
 #include "ui/message_center/message_center.h"
 #include "ui/wm/core/capture_controller.h"
 #include "ui/wm/core/cursor_manager.h"
+#include "ui/wm/core/wm_state.h"
 
 #if defined(OS_CHROMEOS)
 #include "ash/system/chromeos/screen_layout_observer.h"
@@ -73,6 +75,7 @@
 void AshTestHelper::SetUp(bool start_session,
                           MaterialDesignController::Mode material_mode) {
   display::ResetDisplayIdForTest();
+  wm_state_ = base::MakeUnique<::wm::WMState>();
   views_delegate_ = ash_test_environment_->CreateViewsDelegate();
 
   // Disable animations during tests.
diff --git a/ash/test/ash_test_helper.h b/ash/test/ash_test_helper.h
index 5b14e0b..9a0424c6 100644
--- a/ash/test/ash_test_helper.h
+++ b/ash/test/ash_test_helper.h
@@ -24,6 +24,10 @@
 class ViewsDelegate;
 }
 
+namespace wm {
+class WMState;
+}
+
 namespace ash {
 namespace test {
 
@@ -80,6 +84,7 @@
   // Owned by ash::AcceleratorController
   TestScreenshotDelegate* test_screenshot_delegate_;
 
+  std::unique_ptr<::wm::WMState> wm_state_;
   std::unique_ptr<views::ViewsDelegate> views_delegate_;
 
 #if defined(OS_CHROMEOS)
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 3d2c227..b5ed661 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -63,6 +63,7 @@
 #include "ui/aura/client/cursor_client_observer.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/client/screen_position_client.h"
+#include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
@@ -92,7 +93,6 @@
 #include "ui/wm/public/activation_client.h"
 #include "ui/wm/public/scoped_tooltip_disabler.h"
 #include "ui/wm/public/tooltip_client.h"
-#include "ui/wm/public/transient_window_client.h"
 #include "ui/wm/public/window_types.h"
 
 #if defined(OS_WIN)
diff --git a/content/shell/browser/shell.h b/content/shell/browser/shell.h
index d32886d1..6f36810 100644
--- a/content/shell/browser/shell.h
+++ b/content/shell/browser/shell.h
@@ -36,6 +36,9 @@
 class Widget;
 class ViewsDelegate;
 }
+namespace wm {
+class WMState;
+}
 #endif  // defined(USE_AURA)
 
 class GURL;
@@ -253,6 +256,8 @@
 #if defined(OS_CHROMEOS)
   static wm::WMTestHelper* wm_test_helper_;
   static display::Screen* test_screen_;
+#else
+  static wm::WMState* wm_state_;
 #endif
 #if defined(TOOLKIT_VIEWS)
   static views::ViewsDelegate* views_delegate_;
diff --git a/content/shell/browser/shell_views.cc b/content/shell/browser/shell_views.cc
index b25cef8e..c3c95d4 100644
--- a/content/shell/browser/shell_views.cc
+++ b/content/shell/browser/shell_views.cc
@@ -40,6 +40,10 @@
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
 #endif
 
+#if defined(USE_AURA)
+#include "ui/wm/core/wm_state.h"
+#endif
+
 #if defined(OS_WIN)
 #include <fcntl.h>
 #include <io.h>
@@ -307,10 +311,16 @@
 }  // namespace
 
 #if defined(OS_CHROMEOS)
-wm::WMTestHelper* Shell::wm_test_helper_ = NULL;
-display::Screen* Shell::test_screen_ = NULL;
+// static
+wm::WMTestHelper* Shell::wm_test_helper_ = nullptr;
+// static
+display::Screen* Shell::test_screen_ = nullptr;
+#elif defined(USE_AURA)
+// static
+wm::WMState* Shell::wm_state_ = nullptr;
 #endif
-views::ViewsDelegate* Shell::views_delegate_ = NULL;
+// static
+views::ViewsDelegate* Shell::views_delegate_ = nullptr;
 
 // static
 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
@@ -324,6 +334,9 @@
   wm_test_helper_ = new wm::WMTestHelper(default_window_size,
                                          GetContextFactory());
 #else
+#if defined(USE_AURA)
+  wm_state_ = new wm::WMState;
+#endif
   display::Screen::SetScreenInstance(views::CreateDesktopScreen());
 #endif
   views_delegate_ = new views::DesktopTestViewsDelegate();
@@ -332,15 +345,18 @@
 void Shell::PlatformExit() {
 #if defined(OS_CHROMEOS)
   delete wm_test_helper_;
-  wm_test_helper_ = NULL;
+  wm_test_helper_ = nullptr;
 
   delete test_screen_;
-  test_screen_ = NULL;
+  test_screen_ = nullptr;
+#elif defined(USE_AURA)
+  delete wm_state_;
+  wm_state_ = nullptr;
 #endif
   delete views_delegate_;
-  views_delegate_ = NULL;
+  views_delegate_ = nullptr;
   delete platform_;
-  platform_ = NULL;
+  platform_ = nullptr;
 }
 
 void Shell::PlatformCleanUp() {
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index f9ae5022..5862a402 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -25,8 +25,6 @@
     "../wm/public/scoped_tooltip_disabler.h",
     "../wm/public/tooltip_client.cc",
     "../wm/public/tooltip_client.h",
-    "../wm/public/transient_window_client.cc",
-    "../wm/public/transient_window_client.h",
     "../wm/public/window_move_client.cc",
     "../wm/public/window_move_client.h",
     "../wm/public/window_types.h",
@@ -50,6 +48,9 @@
     "client/focus_client.h",
     "client/screen_position_client.cc",
     "client/screen_position_client.h",
+    "client/transient_window_client.cc",
+    "client/transient_window_client.h",
+    "client/transient_window_client_observer.h",
     "client/visibility_client.cc",
     "client/visibility_client.h",
     "client/window_parenting_client.cc",
diff --git a/ui/aura/client/transient_window_client.cc b/ui/aura/client/transient_window_client.cc
new file mode 100644
index 0000000..2859bc3
--- /dev/null
+++ b/ui/aura/client/transient_window_client.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 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 "ui/aura/client/transient_window_client.h"
+
+namespace aura {
+namespace client {
+
+namespace {
+
+TransientWindowClient* instance = nullptr;
+
+}  // namespace
+
+void SetTransientWindowClient(TransientWindowClient* client) {
+  instance = client;
+}
+
+TransientWindowClient* GetTransientWindowClient() {
+  return instance;
+}
+
+}  // namespace client
+}  // namespace aura
diff --git a/ui/aura/client/transient_window_client.h b/ui/aura/client/transient_window_client.h
new file mode 100644
index 0000000..4908856
--- /dev/null
+++ b/ui/aura/client/transient_window_client.h
@@ -0,0 +1,48 @@
+// Copyright 2014 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 UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_H_
+#define UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+
+class Window;
+
+namespace client {
+
+class TransientWindowClientObserver;
+
+// TransientWindowClient is used to add or remove transient windows. Transient
+// children get the following behavior:
+// . The transient parent destroys any transient children when it is
+//   destroyed. This means a transient child is destroyed if either its parent
+//   or transient parent is destroyed.
+// . If a transient child and its transient parent share the same parent, then
+//   transient children are always ordered above the transient parent.
+// Transient windows are typically used for popups and menus.
+class AURA_EXPORT TransientWindowClient {
+ public:
+  virtual void AddTransientChild(Window* parent, Window* child) = 0;
+  virtual void RemoveTransientChild(Window* parent, Window* child) = 0;
+  virtual Window* GetTransientParent(Window* window) = 0;
+  virtual const Window* GetTransientParent(const Window* window) = 0;
+  virtual void AddObserver(TransientWindowClientObserver* observer) = 0;
+  virtual void RemoveObserver(TransientWindowClientObserver* observer) = 0;
+
+ protected:
+  virtual ~TransientWindowClient() {}
+};
+
+// Sets/gets the TransientWindowClient. This does *not* take ownership of
+// |client|. It is assumed the caller will invoke SetTransientWindowClient(NULL)
+// before deleting |client|.
+AURA_EXPORT void SetTransientWindowClient(TransientWindowClient* client);
+AURA_EXPORT TransientWindowClient* GetTransientWindowClient();
+
+}  // namespace client
+}  // namespace aura
+
+#endif  // UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_H_
diff --git a/ui/aura/client/transient_window_client_observer.h b/ui/aura/client/transient_window_client_observer.h
new file mode 100644
index 0000000..27dc39b
--- /dev/null
+++ b/ui/aura/client/transient_window_client_observer.h
@@ -0,0 +1,35 @@
+// Copyright 2016 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 UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_OBSERVER_H_
+#define UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_OBSERVER_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+
+class Window;
+
+namespace client {
+
+class AURA_EXPORT TransientWindowClientObserver {
+ public:
+  // Called when a window is added as a transient child. This is called once
+  // the child is added as a transient, but before any restacking occurs.
+  virtual void OnTransientChildWindowAdded(Window* parent,
+                                           Window* transient_child) = 0;
+
+  // Called when a window is removed as a transient child. This is called once
+  // the child is removed as a transient, but before any restacking occurs.
+  virtual void OnTransientChildWindowRemoved(Window* parent,
+                                             Window* transient_child) = 0;
+
+ protected:
+  virtual ~TransientWindowClientObserver() {}
+};
+
+}  // namespace client
+}  // namespace aura
+
+#endif  // UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_OBSERVER_H_
diff --git a/ui/aura/test/DEPS b/ui/aura/test/DEPS
index f59fd384..523da42 100644
--- a/ui/aura/test/DEPS
+++ b/ui/aura/test/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+ui/gl",
+  "+ui/wm/core/wm_state.h",
 ]
diff --git a/ui/aura/test/aura_test_helper.cc b/ui/aura/test/aura_test_helper.cc
index d3e39e3..4142a11d 100644
--- a/ui/aura/test/aura_test_helper.cc
+++ b/ui/aura/test/aura_test_helper.cc
@@ -4,7 +4,6 @@
 
 #include "ui/aura/test/aura_test_helper.h"
 
-#include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
@@ -29,6 +28,7 @@
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/display/screen.h"
+#include "ui/wm/core/wm_state.h"
 
 #if defined(USE_X11)
 #include "ui/aura/window_tree_host_x11.h"
@@ -70,6 +70,7 @@
 void AuraTestHelper::SetUp(ui::ContextFactory* context_factory) {
   setup_called_ = true;
 
+  wm_state_ = base::MakeUnique<wm::WMState>();
   // Needs to be before creating WindowTreeClient.
   focus_client_ = base::MakeUnique<TestFocusClient>();
   capture_client_ = base::MakeUnique<client::DefaultCaptureClient>();
@@ -137,6 +138,7 @@
     EnvTestHelper(Env::GetInstance())
         .SetWindowPortFactory(Env::WindowPortFactory());
   }
+  wm_state_.reset();
 }
 
 void AuraTestHelper::RunAllPendingInMessageLoop() {
diff --git a/ui/aura/test/aura_test_helper.h b/ui/aura/test/aura_test_helper.h
index 9a117a8..c04f52c 100644
--- a/ui/aura/test/aura_test_helper.h
+++ b/ui/aura/test/aura_test_helper.h
@@ -22,7 +22,12 @@
 class ScopedAnimationDurationScaleMode;
 }
 
+namespace wm {
+class WMState;
+}
+
 namespace aura {
+class Env;
 class TestScreen;
 class TestWindowTree;
 class TestWindowTreeClientSetup;
@@ -84,6 +89,7 @@
   bool teardown_called_;
   std::unique_ptr<TestWindowTreeClientSetup> window_tree_client_setup_;
   std::unique_ptr<aura::Env> env_;
+  std::unique_ptr<wm::WMState> wm_state_;
   std::unique_ptr<WindowTreeHost> host_;
   std::unique_ptr<TestWindowParentingClient> parenting_client_;
   std::unique_ptr<client::DefaultCaptureClient> capture_client_;
diff --git a/ui/views/corewm/tooltip_controller_unittest.cc b/ui/views/corewm/tooltip_controller_unittest.cc
index fe07797..d5d9d1a 100644
--- a/ui/views/corewm/tooltip_controller_unittest.cc
+++ b/ui/views/corewm/tooltip_controller_unittest.cc
@@ -31,7 +31,6 @@
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/default_activation_client.h"
 #include "ui/wm/core/default_screen_position_client.h"
-#include "ui/wm/core/wm_state.h"
 #include "ui/wm/public/tooltip_client.h"
 #include "ui/wm/public/window_types.h"
 
@@ -573,7 +572,6 @@
   ~TooltipControllerTest2() override {}
 
   void SetUp() override {
-    wm_state_.reset(new wm::WMState);
     aura::test::AuraTestBase::SetUp();
     new wm::DefaultActivationClient(root_window());
     controller_.reset(
@@ -591,7 +589,6 @@
     generator_.reset();
     helper_.reset();
     aura::test::AuraTestBase::TearDown();
-    wm_state_.reset();
   }
 
  protected:
@@ -602,7 +599,6 @@
 
  private:
   std::unique_ptr<TooltipController> controller_;
-  std::unique_ptr<wm::WMState> wm_state_;
 
   DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest2);
 };
@@ -648,7 +644,6 @@
   ~TooltipControllerTest3() override {}
 
   void SetUp() override {
-    wm_state_.reset(new wm::WMState);
     ViewsTestBase::SetUp();
 
     aura::Window* root_window = GetContext();
@@ -680,7 +675,6 @@
     helper_.reset();
     widget_.reset();
     ViewsTestBase::TearDown();
-    wm_state_.reset();
   }
 
   aura::Window* GetWindow() { return widget_->GetNativeWindow(); }
@@ -695,7 +689,6 @@
 
  private:
   std::unique_ptr<TooltipController> controller_;
-  std::unique_ptr<wm::WMState> wm_state_;
 
 #if defined(OS_WIN)
   ui::ScopedOleInitializer ole_initializer_;
diff --git a/ui/views/test/test_views_delegate.h b/ui/views/test/test_views_delegate.h
index 5d756bc..e1986ab 100644
--- a/ui/views/test/test_views_delegate.h
+++ b/ui/views/test/test_views_delegate.h
@@ -11,10 +11,6 @@
 #include "build/build_config.h"
 #include "ui/views/views_delegate.h"
 
-namespace wm {
-class WMState;
-}
-
 namespace views {
 
 class TestViewsDelegate : public ViewsDelegate {
@@ -51,10 +47,6 @@
   bool use_desktop_native_widgets_;
   bool use_transparent_windows_;
 
-#if defined(USE_AURA)
-  std::unique_ptr<wm::WMState> wm_state_;
-#endif
-
   DISALLOW_COPY_AND_ASSIGN(TestViewsDelegate);
 };
 
diff --git a/ui/views/test/test_views_delegate_aura.cc b/ui/views/test/test_views_delegate_aura.cc
index db32fd18..9cb1713 100644
--- a/ui/views/test/test_views_delegate_aura.cc
+++ b/ui/views/test/test_views_delegate_aura.cc
@@ -6,7 +6,6 @@
 
 #include "build/build_config.h"
 #include "ui/aura/env.h"
-#include "ui/wm/core/wm_state.h"
 
 #if !defined(OS_CHROMEOS)
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
@@ -19,9 +18,6 @@
     : context_factory_(nullptr),
       use_desktop_native_widgets_(false),
       use_transparent_windows_(false) {
-#if defined(USE_AURA)
-  wm_state_.reset(new wm::WMState);
-#endif
 }
 
 TestViewsDelegate::~TestViewsDelegate() {
diff --git a/ui/views/test/views_test_helper_aura.cc b/ui/views/test/views_test_helper_aura.cc
index f3705b6d..0460cac 100644
--- a/ui/views/test/views_test_helper_aura.cc
+++ b/ui/views/test/views_test_helper_aura.cc
@@ -9,7 +9,6 @@
 #include "ui/wm/core/capture_controller.h"
 #include "ui/wm/core/default_activation_client.h"
 #include "ui/wm/core/default_screen_position_client.h"
-#include "ui/wm/core/wm_state.h"
 
 namespace views {
 
@@ -32,7 +31,6 @@
   aura_test_helper_->SetUp(context_factory_);
   gfx::NativeWindow root_window = GetContext();
   new wm::DefaultActivationClient(root_window);
-  wm_state_.reset(new wm::WMState);
 
   if (!aura::client::GetScreenPositionClient(root_window)) {
     screen_position_client_.reset(new wm::DefaultScreenPositionClient);
@@ -56,7 +54,6 @@
     aura::client::SetScreenPositionClient(GetContext(), nullptr);
 
   aura_test_helper_->TearDown();
-  wm_state_.reset();
   CHECK(!wm::ScopedCaptureClient::IsActive());
 }
 
diff --git a/ui/views/test/views_test_helper_aura.h b/ui/views/test/views_test_helper_aura.h
index 6dfcde6..5acc7ec0 100644
--- a/ui/views/test/views_test_helper_aura.h
+++ b/ui/views/test/views_test_helper_aura.h
@@ -24,10 +24,6 @@
 class MessageLoopForUI;
 }
 
-namespace wm {
-class WMState;
-}
-
 namespace views {
 
 class ViewsTestHelperAura : public ViewsTestHelper {
@@ -44,7 +40,6 @@
  private:
   ui::ContextFactory* context_factory_;
   std::unique_ptr<aura::test::AuraTestHelper> aura_test_helper_;
-  std::unique_ptr<wm::WMState> wm_state_;
   std::unique_ptr<aura::client::ScreenPositionClient> screen_position_client_;
 
   DISALLOW_COPY_AND_ASSIGN(ViewsTestHelperAura);
diff --git a/ui/wm/core/easy_resize_window_targeter.cc b/ui/wm/core/easy_resize_window_targeter.cc
index c286bc2..8071167 100644
--- a/ui/wm/core/easy_resize_window_targeter.cc
+++ b/ui/wm/core/easy_resize_window_targeter.cc
@@ -4,11 +4,11 @@
 
 #include "ui/wm/core/easy_resize_window_targeter.h"
 
+#include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/window.h"
 #include "ui/events/event.h"
 #include "ui/gfx/geometry/insets_f.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/wm/public/transient_window_client.h"
 
 namespace wm {
 
diff --git a/ui/wm/core/focus_controller_unittest.cc b/ui/wm/core/focus_controller_unittest.cc
index c0786b7b..110a47c8 100644
--- a/ui/wm/core/focus_controller_unittest.cc
+++ b/ui/wm/core/focus_controller_unittest.cc
@@ -23,7 +23,6 @@
 #include "ui/events/test/event_generator.h"
 #include "ui/wm/core/base_focus_rules.h"
 #include "ui/wm/core/window_util.h"
-#include "ui/wm/core/wm_state.h"
 #include "ui/wm/public/activation_change_observer.h"
 #include "ui/wm/public/activation_client.h"
 
@@ -372,7 +371,6 @@
 
   // Overridden from aura::test::AuraTestBase:
   void SetUp() override {
-    wm_state_.reset(new wm::WMState);
     // FocusController registers itself as an Env observer so it can catch all
     // window initializations, including the root_window()'s, so we create it
     // before allowing the base setup.
@@ -419,7 +417,6 @@
     aura::test::AuraTestBase::TearDown();
     test_focus_rules_ = NULL;  // Owned by FocusController.
     focus_controller_.reset();
-    wm_state_.reset();
   }
 
   void FocusWindow(aura::Window* window) {
@@ -472,7 +469,6 @@
  private:
   std::unique_ptr<FocusController> focus_controller_;
   TestFocusRules* test_focus_rules_;
-  std::unique_ptr<wm::WMState> wm_state_;
 
   DISALLOW_COPY_AND_ASSIGN(FocusControllerTestBase);
 };
diff --git a/ui/wm/core/shadow_controller_unittest.cc b/ui/wm/core/shadow_controller_unittest.cc
index 3cc1668..a560985 100644
--- a/ui/wm/core/shadow_controller_unittest.cc
+++ b/ui/wm/core/shadow_controller_unittest.cc
@@ -19,7 +19,6 @@
 #include "ui/wm/core/shadow.h"
 #include "ui/wm/core/shadow_types.h"
 #include "ui/wm/core/window_util.h"
-#include "ui/wm/core/wm_state.h"
 #include "ui/wm/public/activation_client.h"
 
 namespace wm {
@@ -30,7 +29,6 @@
   ~ShadowControllerTest() override {}
 
   void SetUp() override {
-    wm_state_.reset(new wm::WMState);
     AuraTestBase::SetUp();
     new wm::DefaultActivationClient(root_window());
     aura::client::ActivationClient* activation_client =
@@ -40,7 +38,6 @@
   void TearDown() override {
     shadow_controller_.reset();
     AuraTestBase::TearDown();
-    wm_state_.reset();
   }
 
  protected:
@@ -55,7 +52,6 @@
 
  private:
   std::unique_ptr<ShadowController> shadow_controller_;
-  std::unique_ptr<wm::WMState> wm_state_;
 
   DISALLOW_COPY_AND_ASSIGN(ShadowControllerTest);
 };
diff --git a/ui/wm/core/transient_window_controller.cc b/ui/wm/core/transient_window_controller.cc
index bb1945c..f1fc00d 100644
--- a/ui/wm/core/transient_window_controller.cc
+++ b/ui/wm/core/transient_window_controller.cc
@@ -4,14 +4,22 @@
 
 #include "ui/wm/core/transient_window_controller.h"
 
+#include "ui/aura/client/transient_window_client_observer.h"
 #include "ui/wm/core/transient_window_manager.h"
 
 namespace wm {
 
+// static
+TransientWindowController* TransientWindowController::instance_ = nullptr;
+
 TransientWindowController::TransientWindowController() {
+  DCHECK(!instance_);
+  instance_ = this;
 }
 
 TransientWindowController::~TransientWindowController() {
+  DCHECK_EQ(instance_, this);
+  instance_ = nullptr;
 }
 
 void TransientWindowController::AddTransientChild(aura::Window* parent,
@@ -37,4 +45,14 @@
   return window_manager ? window_manager->transient_parent() : NULL;
 }
 
+void TransientWindowController::AddObserver(
+    aura::client::TransientWindowClientObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void TransientWindowController::RemoveObserver(
+    aura::client::TransientWindowClientObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
 }  // namespace wm
diff --git a/ui/wm/core/transient_window_controller.h b/ui/wm/core/transient_window_controller.h
index 2ba28bb..1f7a0c4 100644
--- a/ui/wm/core/transient_window_controller.h
+++ b/ui/wm/core/transient_window_controller.h
@@ -6,11 +6,14 @@
 #define UI_WM_CORE_TRANSIENT_WINDOW_CONTROLLER_H_
 
 #include "base/macros.h"
-#include "ui/wm/public/transient_window_client.h"
+#include "base/observer_list.h"
+#include "ui/aura/client/transient_window_client.h"
 #include "ui/wm/wm_export.h"
 
 namespace wm {
 
+class TransientWindowManager;
+
 // TransientWindowClient implementation. Uses TransientWindowManager to handle
 // tracking transient per window.
 class WM_EXPORT TransientWindowController
@@ -19,13 +22,26 @@
   TransientWindowController();
   ~TransientWindowController() override;
 
+  // Returns the single TransientWindowController instance.
+  static TransientWindowController* Get() { return instance_; }
+
   // TransientWindowClient:
   void AddTransientChild(aura::Window* parent, aura::Window* child) override;
   void RemoveTransientChild(aura::Window* parent, aura::Window* child) override;
   aura::Window* GetTransientParent(aura::Window* window) override;
   const aura::Window* GetTransientParent(const aura::Window* window) override;
+  void AddObserver(
+      aura::client::TransientWindowClientObserver* observer) override;
+  void RemoveObserver(
+      aura::client::TransientWindowClientObserver* observer) override;
 
  private:
+  friend class TransientWindowManager;
+
+  static TransientWindowController* instance_;
+
+  base::ObserverList<aura::client::TransientWindowClientObserver> observers_;
+
   DISALLOW_COPY_AND_ASSIGN(TransientWindowController);
 };
 
diff --git a/ui/wm/core/transient_window_manager.cc b/ui/wm/core/transient_window_manager.cc
index 6a829ed..3145c34 100644
--- a/ui/wm/core/transient_window_manager.cc
+++ b/ui/wm/core/transient_window_manager.cc
@@ -9,9 +9,11 @@
 
 #include "base/auto_reset.h"
 #include "base/stl_util.h"
+#include "ui/aura/client/transient_window_client_observer.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_property.h"
 #include "ui/aura/window_tracker.h"
+#include "ui/wm/core/transient_window_controller.h"
 #include "ui/wm/core/transient_window_observer.h"
 #include "ui/wm/core/transient_window_stacking_client.h"
 #include "ui/wm/core/window_util.h"
@@ -67,6 +69,11 @@
   transient_children_.push_back(child);
   child_manager->transient_parent_ = window_;
 
+  for (aura::client::TransientWindowClientObserver& observer :
+       TransientWindowController::Get()->observers_) {
+    observer.OnTransientChildWindowAdded(window_, child);
+  }
+
   // Restack |child| properly above its transient parent, if they share the same
   // parent.
   if (child->parent() == window_->parent())
@@ -83,7 +90,12 @@
   transient_children_.erase(i);
   TransientWindowManager* child_manager = Get(child);
   DCHECK_EQ(window_, child_manager->transient_parent_);
-  child_manager->transient_parent_ = NULL;
+  child_manager->transient_parent_ = nullptr;
+
+  for (aura::client::TransientWindowClientObserver& observer :
+       TransientWindowController::Get()->observers_) {
+    observer.OnTransientChildWindowRemoved(window_, child);
+  }
 
   // If |child| and its former transient parent share the same parent, |child|
   // should be restacked properly so it is not among transient children of its
diff --git a/ui/wm/core/transient_window_manager_unittest.cc b/ui/wm/core/transient_window_manager_unittest.cc
index 041be13..6f25b73 100644
--- a/ui/wm/core/transient_window_manager_unittest.cc
+++ b/ui/wm/core/transient_window_manager_unittest.cc
@@ -14,7 +14,6 @@
 #include "ui/aura/window_observer.h"
 #include "ui/wm/core/transient_window_observer.h"
 #include "ui/wm/core/window_util.h"
-#include "ui/wm/core/wm_state.h"
 
 using aura::Window;
 
@@ -75,16 +74,6 @@
   TransientWindowManagerTest() {}
   ~TransientWindowManagerTest() override {}
 
-  void SetUp() override {
-    AuraTestBase::SetUp();
-    wm_state_.reset(new wm::WMState);
-  }
-
-  void TearDown() override {
-    wm_state_.reset();
-    AuraTestBase::TearDown();
-  }
-
  protected:
   // Creates a transient window that is transient to |parent|.
   Window* CreateTransientChild(int id, Window* parent) {
@@ -98,8 +87,6 @@
   }
 
  private:
-  std::unique_ptr<wm::WMState> wm_state_;
-
   DISALLOW_COPY_AND_ASSIGN(TransientWindowManagerTest);
 };
 
diff --git a/ui/wm/core/wm_state.h b/ui/wm/core/wm_state.h
index 620315c..f82632c 100644
--- a/ui/wm/core/wm_state.h
+++ b/ui/wm/core/wm_state.h
@@ -21,7 +21,6 @@
   WMState();
   ~WMState();
 
-  // WindowStackingClient:
  private:
   std::unique_ptr<TransientWindowStackingClient> window_stacking_client_;
   std::unique_ptr<TransientWindowController> transient_window_client_;
diff --git a/ui/wm/test/wm_test_helper.cc b/ui/wm/test/wm_test_helper.cc
index f0bf2e6..8189500 100644
--- a/ui/wm/test/wm_test_helper.cc
+++ b/ui/wm/test/wm_test_helper.cc
@@ -4,6 +4,7 @@
 
 #include "ui/wm/test/wm_test_helper.h"
 
+#include "base/memory/ptr_util.h"
 #include "ui/aura/client/default_capture_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/test/input_method_glue.h"
@@ -11,11 +12,13 @@
 #include "ui/aura/window.h"
 #include "ui/wm/core/compound_event_filter.h"
 #include "ui/wm/core/default_activation_client.h"
+#include "ui/wm/core/wm_state.h"
 
 namespace wm {
 
 WMTestHelper::WMTestHelper(const gfx::Size& default_window_size,
                            ui::ContextFactory* context_factory) {
+  wm_state_ = base::MakeUnique<WMState>();
   aura::Env::GetInstance()->set_context_factory(context_factory);
   host_.reset(aura::WindowTreeHost::Create(gfx::Rect(default_window_size)));
   host_->InitHost();
diff --git a/ui/wm/test/wm_test_helper.h b/ui/wm/test/wm_test_helper.h
index c03daae..6c6a7eb 100644
--- a/ui/wm/test/wm_test_helper.h
+++ b/ui/wm/test/wm_test_helper.h
@@ -34,6 +34,7 @@
 namespace wm {
 
 class CompoundEventFilter;
+class WMState;
 
 // Creates a minimal environment for running the shell. We can't pull in all of
 // ash here, but we can create attach several of the same things we'd find in
@@ -52,6 +53,7 @@
                                  const gfx::Rect& bounds) override;
 
  private:
+  std::unique_ptr<WMState> wm_state_;
   std::unique_ptr<aura::WindowTreeHost> host_;
   std::unique_ptr<aura::InputMethodGlue> input_method_glue_;
   std::unique_ptr<wm::CompoundEventFilter> root_window_event_filter_;