Revert "Remove NOTIFICATION_FULLSCREEN_CHANGED"

This reverts commit a5646d5a8718a25172eadb5541e1f9386503f793.

Reason for revert:

Crashes 5 tests on Win 7 Tests x64 (1):
https://ci.chromium.org/p/chromium/builders/ci/Win%207%20Tests%20x64%20%281%29/55573

Original change's description:
> Remove NOTIFICATION_FULLSCREEN_CHANGED
> 
> Replace with an observer interface on FullscreenController.
> 
> Bug: 268984
> Change-Id: I1af2056e870af56b2f436a3aa8c6a54ed0ff44fb
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1666148
> Reviewed-by: Scott Violet <sky@chromium.org>
> Commit-Queue: Evan Stade <estade@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#671901}

TBR=sky@chromium.org,estade@chromium.org

Change-Id: I793c04325fa1e4e9e6fb07256e01ff81c22e3b0d
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 268984
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1672139
Reviewed-by: Dan Beam <dbeam@chromium.org>
Commit-Queue: Dan Beam <dbeam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#671920}
diff --git a/chrome/browser/chrome_notification_types.h b/chrome/browser/chrome_notification_types.h
index 92957fd..10c15de 100644
--- a/chrome/browser/chrome_notification_types.h
+++ b/chrome/browser/chrome_notification_types.h
@@ -328,6 +328,9 @@
   // Sent when a ProtocolHandlerRegistry is changed. The source is the profile.
   NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
 
+  // Sent when the browser enters or exits fullscreen mode.
+  NOTIFICATION_FULLSCREEN_CHANGED,
+
   // Sent when the FullscreenController changes, confirms, or denies mouse lock.
   // The source is the browser's FullscreenController, no details.
   NOTIFICATION_MOUSE_LOCK_CHANGED,
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc b/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
index 1856e71..9e6f762 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
@@ -35,6 +35,23 @@
 
 constexpr char kFingerprint[] = "pinky";
 
+class FullscreenWaiter {
+ public:
+  explicit FullscreenWaiter(Browser* browser) : browser_(browser) {}
+  ~FullscreenWaiter() = default;
+
+  void WaitForState(bool fullscreen) {
+    if (browser_->window()->IsFullscreen() != fullscreen)
+      observer_.Wait();
+  }
+
+ private:
+  FullscreenNotificationObserver observer_;
+  Browser* browser_;
+
+  DISALLOW_COPY_AND_ASSIGN(FullscreenWaiter);
+};
+
 class ScreenLockerTest : public InProcessBrowserTest {
  public:
   ScreenLockerTest() = default;
@@ -127,18 +144,20 @@
   ash::wm::WindowState* window_state =
       ash::wm::GetWindowState(browser_window->GetNativeWindow());
   {
-    FullscreenNotificationObserver fullscreen_waiter(browser());
+    FullscreenWaiter fullscreen_waiter(browser());
     browser()
         ->exclusive_access_manager()
         ->fullscreen_controller()
         ->ToggleBrowserFullscreenMode();
-    fullscreen_waiter.Wait();
+    fullscreen_waiter.WaitForState(true);
     EXPECT_TRUE(browser_window->IsFullscreen());
     EXPECT_FALSE(window_state->GetHideShelfWhenFullscreen());
     EXPECT_FALSE(tester.IsLocked());
   }
   {
+    FullscreenWaiter fullscreen_waiter(browser());
     tester.Lock();
+    fullscreen_waiter.WaitForState(true);
     EXPECT_TRUE(browser_window->IsFullscreen());
     EXPECT_FALSE(window_state->GetHideShelfWhenFullscreen());
     EXPECT_TRUE(tester.IsLocked());
@@ -147,12 +166,12 @@
   tester.UnlockWithPassword(user_manager::StubAccountId(), "pass");
   EXPECT_FALSE(tester.IsLocked());
   {
-    FullscreenNotificationObserver fullscreen_waiter(browser());
+    FullscreenWaiter fullscreen_waiter(browser());
     browser()
         ->exclusive_access_manager()
         ->fullscreen_controller()
         ->ToggleBrowserFullscreenMode();
-    fullscreen_waiter.Wait();
+    fullscreen_waiter.WaitForState(false);
     EXPECT_FALSE(browser_window->IsFullscreen());
   }
 
@@ -164,20 +183,22 @@
   // has all of the pixels, locking the screen should exit fullscreen. The
   // fullscreen window has all of the pixels when in tab fullscreen.
   {
-    FullscreenNotificationObserver fullscreen_waiter(browser());
+    FullscreenWaiter fullscreen_waiter(browser());
     content::WebContents* web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
     browser()
         ->exclusive_access_manager()
         ->fullscreen_controller()
         ->EnterFullscreenModeForTab(web_contents, GURL());
-    fullscreen_waiter.Wait();
+    fullscreen_waiter.WaitForState(true);
     EXPECT_TRUE(browser_window->IsFullscreen());
     EXPECT_TRUE(window_state->GetHideShelfWhenFullscreen());
     EXPECT_FALSE(tester.IsLocked());
   }
   {
+    FullscreenWaiter fullscreen_waiter(browser());
     tester.Lock();
+    fullscreen_waiter.WaitForState(false);
     EXPECT_FALSE(browser_window->IsFullscreen());
     EXPECT_TRUE(tester.IsLocked());
   }
diff --git a/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc b/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
index 05538d5..b0ab624 100644
--- a/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
+++ b/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
@@ -686,10 +686,11 @@
   AddResizeListener(child, GetScreenSize());
   {
     content::DOMMessageQueue queue;
-    FullscreenNotificationObserver observer(browser());
+    std::unique_ptr<FullscreenNotificationObserver> observer(
+        new FullscreenNotificationObserver());
     EXPECT_TRUE(ExecuteScript(child, "activateFullscreen()"));
     WaitForMultipleFullscreenEvents(expected_events, queue);
-    observer.Wait();
+    observer->Wait();
   }
 
   // Verify that the browser has entered fullscreen for the current tab.
@@ -721,10 +722,11 @@
   AddResizeListener(child, original_child_size);
   {
     content::DOMMessageQueue queue;
-    FullscreenNotificationObserver observer(browser());
+    std::unique_ptr<FullscreenNotificationObserver> observer(
+        new FullscreenNotificationObserver());
     EXPECT_TRUE(ExecuteScript(child, "exitFullscreen()"));
     WaitForMultipleFullscreenEvents(expected_events, queue);
-    observer.Wait();
+    observer->Wait();
   }
 
   EXPECT_FALSE(browser()->window()->IsFullscreen());
@@ -784,10 +786,11 @@
   std::set<std::string> expected_events = {"main_frame", "child", "grandchild"};
   {
     content::DOMMessageQueue queue;
-    FullscreenNotificationObserver observer(browser());
+    std::unique_ptr<FullscreenNotificationObserver> observer(
+        new FullscreenNotificationObserver());
     EXPECT_TRUE(ExecuteScript(grandchild, "activateFullscreen()"));
     WaitForMultipleFullscreenEvents(expected_events, queue);
-    observer.Wait();
+    observer->Wait();
   }
 
   // Verify that the browser has entered fullscreen for the current tab.
@@ -814,7 +817,8 @@
   AddResizeListener(grandchild, original_grandchild_size);
   {
     content::DOMMessageQueue queue;
-    FullscreenNotificationObserver observer(browser());
+    std::unique_ptr<FullscreenNotificationObserver> observer(
+        new FullscreenNotificationObserver());
     switch (exit_method) {
       case FullscreenExitMethod::JS_CALL:
         EXPECT_TRUE(ExecuteScript(grandchild, "exitFullscreen()"));
@@ -827,7 +831,7 @@
         NOTREACHED();
     }
     WaitForMultipleFullscreenEvents(expected_events, queue);
-    observer.Wait();
+    observer->Wait();
   }
 
   EXPECT_FALSE(browser()->window()->IsFullscreen());
@@ -946,10 +950,11 @@
   // browser finishes the fullscreen transition.
   {
     content::DOMMessageQueue queue;
-    FullscreenNotificationObserver observer(browser());
+    std::unique_ptr<FullscreenNotificationObserver> observer(
+        new FullscreenNotificationObserver());
     EXPECT_TRUE(ExecuteScript(c_middle, "activateFullscreen()"));
     WaitForMultipleFullscreenEvents(expected_events, queue);
-    observer.Wait();
+    observer->Wait();
   }
 
   // Verify that the browser has entered fullscreen for the current tab.
@@ -986,11 +991,12 @@
   AddResizeListener(c_middle, c_middle_original_size);
   {
     content::DOMMessageQueue queue;
-    FullscreenNotificationObserver observer(browser());
+    std::unique_ptr<FullscreenNotificationObserver> observer(
+        new FullscreenNotificationObserver());
     ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE,
                                                 false, false, false, false));
     WaitForMultipleFullscreenEvents(expected_events, queue);
-    observer.Wait();
+    observer->Wait();
   }
 
   EXPECT_FALSE(browser()->window()->IsFullscreen());
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 09b8f64..6cbb609f 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -876,6 +876,7 @@
       "exclusive_access/exclusive_access_bubble_hide_callback.h",
       "exclusive_access/exclusive_access_bubble_type.cc",
       "exclusive_access/exclusive_access_bubble_type.h",
+      "exclusive_access/exclusive_access_context.cc",
       "exclusive_access/exclusive_access_context.h",
       "exclusive_access/exclusive_access_controller_base.cc",
       "exclusive_access/exclusive_access_controller_base.h",
@@ -883,7 +884,6 @@
       "exclusive_access/exclusive_access_manager.h",
       "exclusive_access/fullscreen_controller.cc",
       "exclusive_access/fullscreen_controller.h",
-      "exclusive_access/fullscreen_observer.h",
       "exclusive_access/fullscreen_within_tab_helper.cc",
       "exclusive_access/fullscreen_within_tab_helper.h",
       "exclusive_access/keyboard_lock_controller.cc",
diff --git a/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm
index c933315a..a28cace 100644
--- a/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm
+++ b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm
@@ -48,28 +48,21 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PermissionBubbleBrowserTest,
-                       TabFullscreenHasNoLocationBar) {
+                       TabFullscreenHasLocationBar) {
+  FullscreenNotificationObserver fullscreen_observer;
   ShowBubble(browser());
   EXPECT_TRUE(HasVisibleLocationBarForBrowser(browser()));
 
   FullscreenController* controller =
       browser()->exclusive_access_manager()->fullscreen_controller();
-  {
-    FullscreenNotificationObserver fullscreen_observer(browser());
-    controller->EnterFullscreenModeForTab(
-        browser()->tab_strip_model()->GetActiveWebContents(), GURL());
-    fullscreen_observer.Wait();
-  }
-  EXPECT_TRUE(controller->IsTabFullscreen());
+  controller->EnterFullscreenModeForTab(
+      browser()->tab_strip_model()->GetActiveWebContents(), GURL());
+  fullscreen_observer.Wait();
   EXPECT_FALSE(HasVisibleLocationBarForBrowser(browser()));
 
-  {
-    FullscreenNotificationObserver fullscreen_observer(browser());
-    controller->ExitFullscreenModeForTab(
-        browser()->tab_strip_model()->GetActiveWebContents());
-    fullscreen_observer.Wait();
-  }
-  EXPECT_FALSE(controller->IsTabFullscreen());
+  controller->ExitFullscreenModeForTab(
+      browser()->tab_strip_model()->GetActiveWebContents());
+  fullscreen_observer.Wait();
   EXPECT_TRUE(HasVisibleLocationBarForBrowser(browser()));
 }
 
diff --git a/chrome/browser/ui/exclusive_access/exclusive_access_context.cc b/chrome/browser/ui/exclusive_access/exclusive_access_context.cc
new file mode 100644
index 0000000..6add97c
--- /dev/null
+++ b/chrome/browser/ui/exclusive_access/exclusive_access_context.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 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 "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+// This file provides default implementations for the ExclusiveAccessContext
+// methods that only some platforms care about.
+
+void ExclusiveAccessContext::UpdateUIForTabFullscreen(
+    TabFullscreenState state) {
+  NOTIMPLEMENTED();
+}
+
+void ExclusiveAccessContext::UpdateFullscreenToolbar() {
+  NOTIMPLEMENTED();
+}
diff --git a/chrome/browser/ui/exclusive_access/exclusive_access_context.h b/chrome/browser/ui/exclusive_access/exclusive_access_context.h
index 35ce7d0..08155f9 100644
--- a/chrome/browser/ui/exclusive_access/exclusive_access_context.h
+++ b/chrome/browser/ui/exclusive_access/exclusive_access_context.h
@@ -26,7 +26,7 @@
     STATE_EXIT_TAB_FULLSCREEN,
   };
 
-  virtual ~ExclusiveAccessContext() = default;
+  virtual ~ExclusiveAccessContext() {}
 
   // Returns the current profile associated with the window.
   virtual Profile* GetProfile() = 0;
@@ -38,11 +38,11 @@
   // Called when we transition between tab and browser fullscreen. This method
   // updates the UI by showing/hiding the tab strip, toolbar and bookmark bar
   // in the browser fullscreen. Currently only supported on Mac.
-  virtual void UpdateUIForTabFullscreen(TabFullscreenState state) {}
+  virtual void UpdateUIForTabFullscreen(TabFullscreenState state);
 
   // Updates the toolbar state to be hidden or shown in fullscreen according to
   // the preference's state. Only supported on Mac.
-  virtual void UpdateFullscreenToolbar() {}
+  virtual void UpdateFullscreenToolbar();
 
   // Enters fullscreen and update exit bubble.
   virtual void EnterFullscreen(const GURL& url,
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
index 88f2f3e..c3649e9 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
@@ -13,6 +13,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
@@ -25,6 +26,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
@@ -47,16 +49,16 @@
 }  // namespace
 
 FullscreenController::FullscreenController(ExclusiveAccessManager* manager)
-    : ExclusiveAccessControllerBase(manager) {}
+    : ExclusiveAccessControllerBase(manager),
+      state_prior_to_tab_fullscreen_(STATE_INVALID),
+      tab_fullscreen_(false),
+      toggled_into_fullscreen_(false),
+      deactivated_contents_(nullptr),
+      is_privileged_fullscreen_for_testing_(false),
+      is_tab_fullscreen_for_testing_(false),
+      ptr_factory_(this) {}
 
-FullscreenController::~FullscreenController() = default;
-
-void FullscreenController::AddObserver(FullscreenObserver* observer) {
-  observer_list_.AddObserver(observer);
-}
-
-void FullscreenController::RemoveObserver(FullscreenObserver* observer) {
-  observer_list_.RemoveObserver(observer);
+FullscreenController::~FullscreenController() {
 }
 
 bool FullscreenController::IsFullscreenForBrowser() const {
@@ -160,7 +162,7 @@
 
   // This is only a change between Browser and Tab fullscreen. We generate
   // a fullscreen notification now because there is no window change.
-  PostFullscreenChangeNotification();
+  PostFullscreenChangeNotification(true);
 }
 
 void FullscreenController::ExitFullscreenModeForTab(WebContents* web_contents) {
@@ -206,7 +208,7 @@
 
   // This is only a change between Browser and Tab fullscreen. We generate
   // a fullscreen notification now because there is no window change.
-  PostFullscreenChangeNotification();
+  PostFullscreenChangeNotification(true);
 }
 
 void FullscreenController::OnTabDeactivated(
@@ -271,7 +273,7 @@
       exclusive_access_manager()->context();
   bool exiting_fullscreen = !exclusive_access_context->IsFullscreen();
 
-  PostFullscreenChangeNotification();
+  PostFullscreenChangeNotification(!exiting_fullscreen);
   if (exiting_fullscreen) {
     toggled_into_fullscreen_ = false;
     extension_caused_fullscreen_ = GURL();
@@ -315,15 +317,18 @@
     NotifyTabExclusiveAccessLost();
 }
 
-void FullscreenController::PostFullscreenChangeNotification() {
+void FullscreenController::PostFullscreenChangeNotification(
+    bool is_fullscreen) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(&FullscreenController::NotifyFullscreenChange,
-                                ptr_factory_.GetWeakPtr()));
+                                ptr_factory_.GetWeakPtr(), is_fullscreen));
 }
 
-void FullscreenController::NotifyFullscreenChange() {
-  for (auto& observer : observer_list_)
-    observer.OnFullscreenStateChanged();
+void FullscreenController::NotifyFullscreenChange(bool is_fullscreen) {
+  content::NotificationService::current()->Notify(
+      chrome::NOTIFICATION_FULLSCREEN_CHANGED,
+      content::Source<FullscreenController>(this),
+      content::Details<bool>(&is_fullscreen));
 }
 
 void FullscreenController::NotifyTabExclusiveAccessLost() {
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.h b/chrome/browser/ui/exclusive_access/fullscreen_controller.h
index f5a58fd..5123171 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.h
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.h
@@ -9,10 +9,11 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_controller_base.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_observer.h"
 #include "components/content_settings/core/common/content_settings.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
 
 class GURL;
 
@@ -62,9 +63,6 @@
   explicit FullscreenController(ExclusiveAccessManager* manager);
   ~FullscreenController() override;
 
-  void AddObserver(FullscreenObserver* observer);
-  void RemoveObserver(FullscreenObserver* observer);
-
   // Browser/User Fullscreen ///////////////////////////////////////////////////
 
   // Returns true if the window is currently fullscreen and was initially
@@ -158,9 +156,10 @@
     TAB
   };
 
-  // Posts a task to notify observers of the fullscreen state change.
-  void PostFullscreenChangeNotification();
-  void NotifyFullscreenChange();
+  // Posts a task to call NotifyFullscreenChange.
+  void PostFullscreenChangeNotification(bool is_fullscreen);
+  // Sends a NOTIFICATION_FULLSCREEN_CHANGED notification.
+  void NotifyFullscreenChange(bool is_fullscreen);
 
   // Notifies the tab that it has been forced out of fullscreen mode if
   // necessary.
@@ -199,27 +198,25 @@
   };
   // The state before entering tab fullscreen mode via webkitRequestFullScreen.
   // When not in tab fullscreen, it is STATE_INVALID.
-  PriorFullscreenState state_prior_to_tab_fullscreen_ = STATE_INVALID;
+  PriorFullscreenState state_prior_to_tab_fullscreen_;
   // True if the site has entered into fullscreen.
-  bool tab_fullscreen_ = false;
+  bool tab_fullscreen_;
 
   // True if this controller has toggled into tab OR browser fullscreen.
-  bool toggled_into_fullscreen_ = false;
+  bool toggled_into_fullscreen_;
 
   // Set in OnTabDeactivated(). Used to see if we're in the middle of
   // deactivation of a tab.
-  content::WebContents* deactivated_contents_ = nullptr;
+  content::WebContents* deactivated_contents_;
 
   // Used in testing to confirm proper behavior for specific, privileged
   // fullscreen cases.
-  bool is_privileged_fullscreen_for_testing_ = false;
+  bool is_privileged_fullscreen_for_testing_;
 
   // Used in testing to set the state to tab fullscreen.
-  bool is_tab_fullscreen_for_testing_ = false;
+  bool is_tab_fullscreen_for_testing_;
 
-  base::ObserverList<FullscreenObserver> observer_list_;
-
-  base::WeakPtrFactory<FullscreenController> ptr_factory_{this};
+  base::WeakPtrFactory<FullscreenController> ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(FullscreenController);
 };
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
index 297653d..ac813a6 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
@@ -90,7 +90,7 @@
 void FullscreenControllerInteractiveTest::ToggleBrowserFullscreen(
     bool enter_fullscreen) {
   ASSERT_EQ(browser()->window()->IsFullscreen(), !enter_fullscreen);
-  FullscreenNotificationObserver fullscreen_observer(browser());
+  FullscreenNotificationObserver fullscreen_observer;
 
   chrome::ToggleFullscreenMode(browser());
 
@@ -103,7 +103,7 @@
     bool enter_fullscreen, bool retry_until_success) {
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   do {
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     if (enter_fullscreen)
       browser()->EnterFullscreenModeForTab(tab, GURL(),
                                            blink::WebFullscreenOptions());
@@ -133,7 +133,7 @@
   ASSERT_NO_FATAL_FAILURE(ToggleTabFullscreen(true));
 
   {
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     AddTabAtIndex(1, GURL(url::kAboutBlankURL), PAGE_TRANSITION_TYPED);
     fullscreen_observer.Wait();
     ASSERT_FALSE(browser()->window()->IsFullscreen());
@@ -264,7 +264,7 @@
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
 
   {
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     EXPECT_FALSE(browser()->window()->IsFullscreen());
     browser()->EnterFullscreenModeForTab(tab, GURL(),
                                          blink::WebFullscreenOptions());
@@ -273,7 +273,7 @@
   }
 
   {
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     chrome::ToggleFullscreenMode(browser());
     fullscreen_observer.Wait();
     EXPECT_FALSE(browser()->window()->IsFullscreen());
@@ -282,7 +282,7 @@
   {
     // Test that tab fullscreen mode doesn't make presentation mode the default
     // on Lion.
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     chrome::ToggleFullscreenMode(browser());
     fullscreen_observer.Wait();
     EXPECT_TRUE(browser()->window()->IsFullscreen());
@@ -326,7 +326,7 @@
 
   // Request to lock the mouse and enter fullscreen.
   {
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
         browser(), ui::VKEY_B, false, true, false, false,
         chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
@@ -336,7 +336,7 @@
 
   // Escape, no prompts should remain.
   {
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     SendEscapeToFullscreenController();
     fullscreen_observer.Wait();
   }
@@ -399,7 +399,7 @@
 
   // Request to lock the mouse and enter fullscreen.
   {
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
         browser(), ui::VKEY_B, false, true, false, false,
         chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
@@ -425,7 +425,7 @@
   SetPrivilegedFullscreen(true);
 
   // Request to lock the mouse and enter fullscreen.
-  FullscreenNotificationObserver fullscreen_observer(browser());
+  FullscreenNotificationObserver fullscreen_observer;
   ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
       browser(), ui::VKEY_B, false, true, false, false,
       chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
@@ -637,7 +637,7 @@
 
   // Request to lock the mouse and enter fullscreen.
   {
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
         browser(), ui::VKEY_B, false, true, false, false,
         chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
@@ -650,7 +650,7 @@
 
   // Reload. Mouse should be unlocked and fullscreen exited.
   {
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     Reload();
     fullscreen_observer.Wait();
     ASSERT_FALSE(IsMouseLocked());
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_state_interactive_browsertest.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_state_interactive_browsertest.cc
index 866e113..5c14228 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_state_interactive_browsertest.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_state_interactive_browsertest.cc
@@ -31,23 +31,16 @@
 class FullscreenControllerStateInteractiveTest
     : public InProcessBrowserTest,
       public FullscreenControllerStateTest {
- public:
-  FullscreenControllerStateInteractiveTest() = default;
-  ~FullscreenControllerStateInteractiveTest() override = default;
-
-  // InProcessBrowserTest:
-  void TearDown() override {
-    FullscreenControllerStateTest::TearDown();
-    InProcessBrowserTest::TearDown();
-  }
-
-  // FullscreenControllerStateTest:
-  Browser* GetBrowser() override { return InProcessBrowserTest::browser(); }
-
  private:
-  DISALLOW_COPY_AND_ASSIGN(FullscreenControllerStateInteractiveTest);
+  // FullscreenControllerStateTest override:
+  Browser* GetBrowser() override;
 };
 
+Browser* FullscreenControllerStateInteractiveTest::GetBrowser() {
+  return InProcessBrowserTest::browser();
+}
+
+
 // Soak tests ------------------------------------------------------------------
 
 // Tests all states with all permutations of multiple events to detect lingering
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_state_test.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_state_test.cc
index 0e835523..232fe93 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_state_test.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_state_test.cc
@@ -227,12 +227,12 @@
 
 bool FullscreenControllerStateTest::InvokeEvent(Event event) {
   if (!fullscreen_notification_observer_.get()) {
-    // Start observing fullscreen changes. Construct the notification observer
-    // here instead of in
+    // Start observing NOTIFICATION_FULLSCREEN_CHANGED. Construct the
+    // notification observer here instead of in
     // FullscreenControllerStateTest::FullscreenControllerStateTest() so that we
     // listen to notifications on the proper thread.
-    fullscreen_notification_observer_ =
-        std::make_unique<FullscreenNotificationObserver>(GetBrowser());
+    fullscreen_notification_observer_.reset(
+        new FullscreenNotificationObserver());
   }
 
   State source_state = state_;
@@ -350,8 +350,8 @@
       IsPersistentState(state_)) {
     fullscreen_notification_observer_->Wait();
     last_notification_received_state_ = state_;
-    fullscreen_notification_observer_ =
-        std::make_unique<FullscreenNotificationObserver>(GetBrowser());
+    fullscreen_notification_observer_.reset(
+        new FullscreenNotificationObserver());
   }
 }
 
@@ -507,10 +507,6 @@
   }
 }
 
-void FullscreenControllerStateTest::TearDown() {
-  fullscreen_notification_observer_.reset();
-}
-
 FullscreenController* FullscreenControllerStateTest::GetFullscreenController() {
   return GetBrowser()->exclusive_access_manager()->fullscreen_controller();
 }
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_state_test.h b/chrome/browser/ui/exclusive_access/fullscreen_controller_state_test.h
index 4f52b67..679e221 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_state_test.h
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_state_test.h
@@ -100,8 +100,8 @@
   // Checks that window state matches the expected controller state.
   virtual void VerifyWindowState();
 
-  // Wait for a fullscreen change if a notification should have been sent in
-  // transitioning to |state_| from the previous persistent state.
+  // Wait for NOTIFICATION_FULLSCREEN_CHANGED if a notification should have been
+  // sent in transitioning to |state_| from the previous persistent state.
   void MaybeWaitForNotification();
 
   // Tests all states with all permutations of multiple events to detect
@@ -165,8 +165,6 @@
       FullscreenForBrowserExpectation fullscreen_for_browser,
       FullscreenForTabExpectation fullscreen_for_tab);
 
-  void TearDown();
-
   virtual Browser* GetBrowser() = 0;
   FullscreenController* GetFullscreenController();
 
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_state_unittest.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_state_unittest.cc
index c8382ab..5efe7279 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_state_unittest.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_state_unittest.cc
@@ -216,7 +216,6 @@
 
   // FullscreenControllerStateTest:
   void SetUp() override;
-  void TearDown() override;
   std::unique_ptr<BrowserWindow> CreateBrowserWindow() override;
   void ChangeWindowFullscreenState() override;
   const char* GetWindowStateString() override;
@@ -226,24 +225,18 @@
   // FullscreenControllerStateTest:
   bool ShouldSkipStateAndEventPair(State state, Event event) override;
   Browser* GetBrowser() override;
-  FullscreenControllerTestWindow* window_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(FullscreenControllerStateUnitTest);
+  FullscreenControllerTestWindow* window_;
 };
 
-FullscreenControllerStateUnitTest::FullscreenControllerStateUnitTest() =
-    default;
+FullscreenControllerStateUnitTest::FullscreenControllerStateUnitTest()
+    : window_(NULL) {
+}
 
 void FullscreenControllerStateUnitTest::SetUp() {
   BrowserWithTestWindowTest::SetUp();
   window_->set_browser(browser());
 }
 
-void FullscreenControllerStateUnitTest::TearDown() {
-  FullscreenControllerStateTest::TearDown();
-  BrowserWithTestWindowTest::TearDown();
-}
-
 std::unique_ptr<BrowserWindow>
 FullscreenControllerStateUnitTest::CreateBrowserWindow() {
   auto window = std::make_unique<FullscreenControllerTestWindow>();
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.cc
index ffd2375..c9f5f02 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.cc
@@ -33,26 +33,6 @@
 
 using content::WebContents;
 
-FullscreenNotificationObserver::FullscreenNotificationObserver(
-    Browser* browser) {
-  observer_.Add(browser->exclusive_access_manager()->fullscreen_controller());
-}
-
-FullscreenNotificationObserver::~FullscreenNotificationObserver() = default;
-
-void FullscreenNotificationObserver::OnFullscreenStateChanged() {
-  observed_change_ = true;
-  if (run_loop_.running())
-    run_loop_.Quit();
-}
-
-void FullscreenNotificationObserver::Wait() {
-  if (observed_change_)
-    return;
-
-  run_loop_.Run();
-}
-
 const char FullscreenControllerTest::kFullscreenKeyboardLockHTML[] =
     "/fullscreen_keyboardlock/fullscreen_keyboardlock.html";
 
@@ -193,20 +173,20 @@
 
 void FullscreenControllerTest::EnterActiveTabFullscreen() {
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
-  FullscreenNotificationObserver fullscreen_observer(browser());
+  FullscreenNotificationObserver fullscreen_observer;
   browser()->EnterFullscreenModeForTab(tab, GURL(),
                                        blink::WebFullscreenOptions());
   fullscreen_observer.Wait();
 }
 
 void FullscreenControllerTest::ToggleBrowserFullscreen() {
-  FullscreenNotificationObserver fullscreen_observer(browser());
+  FullscreenNotificationObserver fullscreen_observer;
   chrome::ToggleFullscreenMode(browser());
   fullscreen_observer.Wait();
 }
 
 void FullscreenControllerTest::EnterExtensionInitiatedFullscreen() {
-  FullscreenNotificationObserver fullscreen_observer(browser());
+  FullscreenNotificationObserver fullscreen_observer;
   browser()->ToggleFullscreenModeWithExtension(GURL("faux_extension"));
   fullscreen_observer.Wait();
 }
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
index e7de20b..abe629b9 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
@@ -10,15 +10,12 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/run_loop.h"
-#include "base/scoped_observer.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_bubble_hide_callback.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_bubble_type.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_observer.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
@@ -27,30 +24,18 @@
 #include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
 #endif
 
-class Browser;
-
 namespace base {
 class TickClock;
 }  // namespace base
 
-// Observer for fullscreen state change notifications.
-class FullscreenNotificationObserver : public FullscreenObserver {
+// Observer for NOTIFICATION_FULLSCREEN_CHANGED notifications.
+class FullscreenNotificationObserver
+    : public content::WindowedNotificationObserver {
  public:
-  explicit FullscreenNotificationObserver(Browser* browser);
-  ~FullscreenNotificationObserver() override;
-
-  // Runs a loop until a fullscreen change is seen (unless one has already been
-  // observed, in which case it returns immediately).
-  void Wait();
-
-  // FullscreenObserver:
-  void OnFullscreenStateChanged() override;
-
+  FullscreenNotificationObserver() : WindowedNotificationObserver(
+      chrome::NOTIFICATION_FULLSCREEN_CHANGED,
+      content::NotificationService::AllSources()) {}
  protected:
-  bool observed_change_ = false;
-  ScopedObserver<FullscreenController, FullscreenObserver> observer_{this};
-  base::RunLoop run_loop_;
-
   DISALLOW_COPY_AND_ASSIGN(FullscreenNotificationObserver);
 };
 
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_observer.h b/chrome/browser/ui/exclusive_access/fullscreen_observer.h
deleted file mode 100644
index d35298461..0000000
--- a/chrome/browser/ui/exclusive_access/fullscreen_observer.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2019 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 CHROME_BROWSER_UI_EXCLUSIVE_ACCESS_FULLSCREEN_OBSERVER_H_
-#define CHROME_BROWSER_UI_EXCLUSIVE_ACCESS_FULLSCREEN_OBSERVER_H_
-
-#include "base/observer_list_types.h"
-
-// An interface to be notified of changes in FullscreenController.
-class FullscreenObserver : public base::CheckedObserver {
- public:
-  virtual void OnFullscreenStateChanged() = 0;
-};
-
-#endif  // CHROME_BROWSER_UI_EXCLUSIVE_ACCESS_FULLSCREEN_OBSERVER_H_
diff --git a/chrome/browser/ui/fullscreen_keyboard_browsertest_base.cc b/chrome/browser/ui/fullscreen_keyboard_browsertest_base.cc
index 6a1716c..8fada4c 100644
--- a/chrome/browser/ui/fullscreen_keyboard_browsertest_base.cc
+++ b/chrome/browser/ui/fullscreen_keyboard_browsertest_base.cc
@@ -11,13 +11,14 @@
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
+#include "content/public/browser/notification_service.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
@@ -180,7 +181,9 @@
 void FullscreenKeyboardBrowserTestBase::SendFullscreenShortcutAndWait() {
   // On MacOSX, entering and exiting fullscreen are not synchronous. So we wait
   // for the observer to notice the change of fullscreen state.
-  FullscreenNotificationObserver observer(GetActiveBrowser());
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_FULLSCREEN_CHANGED,
+      content::NotificationService::AllSources());
 // Enter fullscreen.
 #if defined(OS_MACOSX)
   // On MACOSX, Command + Control + F is used.
@@ -199,14 +202,16 @@
 // keyboard events. As a result, content doesn't actually know it has entered
 // fullscreen. For more details, see ScopedFakeNSWindowFullscreen.
 // TODO(crbug.com/837438): Remove this once ScopedFakeNSWindowFullscreen fires
-// OnFullscreenStateChanged.
+// NOTIFICATION_FULLSCREEN_CHANGED.
 #if !defined(OS_MACOSX)
   observer.Wait();
 #endif
 }
 
 void FullscreenKeyboardBrowserTestBase::SendJsFullscreenShortcutAndWait() {
-  FullscreenNotificationObserver observer(GetActiveBrowser());
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_FULLSCREEN_CHANGED,
+      content::NotificationService::AllSources());
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(GetActiveBrowser(), ui::VKEY_S,
                                               false, false, false, false));
   expected_result_ += "KeyS ctrl:false shift:false alt:false meta:false\n";
@@ -222,7 +227,9 @@
 
 void FullscreenKeyboardBrowserTestBase::
     SendEscapeAndWaitForExitingFullscreen() {
-  FullscreenNotificationObserver observer(GetActiveBrowser());
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_FULLSCREEN_CHANGED,
+      content::NotificationService::AllSources());
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
       GetActiveBrowser(), ui::VKEY_ESCAPE, false, false, false, false));
   observer.Wait();
diff --git a/chrome/browser/ui/views/exclusive_access_bubble_views.cc b/chrome/browser/ui/views/exclusive_access_bubble_views.cc
index bd0afd5..53827b7 100644
--- a/chrome/browser/ui/views/exclusive_access_bubble_views.cc
+++ b/chrome/browser/ui/views/exclusive_access_bubble_views.cc
@@ -14,6 +14,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/views/exclusive_access_bubble_views_context.h"
@@ -21,6 +22,7 @@
 #include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "chrome/browser/ui/views/subtle_notification_view.h"
 #include "chrome/grit/generated_resources.h"
+#include "content/public/browser/notification_service.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/keycodes/keyboard_codes.h"
@@ -68,8 +70,10 @@
   view_->SetBounds(0, 0, size.width(), size.height());
   popup_->AddObserver(this);
 
-  fullscreen_observer_.Add(bubble_view_context_->GetExclusiveAccessManager()
-                               ->fullscreen_controller());
+  registrar_.Add(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED,
+                 content::Source<FullscreenController>(
+                     bubble_view_context_->GetExclusiveAccessManager()
+                         ->fullscreen_controller()));
 
   UpdateMouseWatcher();
 }
@@ -271,7 +275,11 @@
   return bubble_view_context_->CanTriggerOnMouse();
 }
 
-void ExclusiveAccessBubbleViews::OnFullscreenStateChanged() {
+void ExclusiveAccessBubbleViews::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type);
   UpdateMouseWatcher();
 }
 
diff --git a/chrome/browser/ui/views/exclusive_access_bubble_views.h b/chrome/browser/ui/views/exclusive_access_bubble_views.h
index 694401a..25221f71 100644
--- a/chrome/browser/ui/views/exclusive_access_bubble_views.h
+++ b/chrome/browser/ui/views/exclusive_access_bubble_views.h
@@ -9,11 +9,10 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/scoped_observer.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_bubble.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_bubble_hide_callback.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_observer.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
 #include "ui/views/widget/widget_observer.h"
 
 class ExclusiveAccessBubbleViewsContext;
@@ -33,7 +32,7 @@
 // a click target. The bubble auto-hides, and re-shows when the user moves to
 // the screen top.
 class ExclusiveAccessBubbleViews : public ExclusiveAccessBubble,
-                                   public FullscreenObserver,
+                                   public content::NotificationObserver,
                                    public views::WidgetObserver {
  public:
   ExclusiveAccessBubbleViews(
@@ -85,8 +84,10 @@
   bool IsAnimating() override;
   bool CanTriggerOnMouse() const override;
 
-  // FullscreenObserver:
-  void OnFullscreenStateChanged() override;
+  // content::NotificationObserver:
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override;
 
   // views::WidgetObserver:
   void OnWidgetDestroyed(views::Widget* widget) override;
@@ -110,8 +111,7 @@
   SubtleNotificationView* view_;
   base::string16 browser_fullscreen_exit_accelerator_;
 
-  ScopedObserver<FullscreenController, FullscreenObserver> fullscreen_observer_{
-      this};
+  content::NotificationRegistrar registrar_;
 
   DISALLOW_COPY_AND_ASSIGN(ExclusiveAccessBubbleViews);
 };
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 963219b..57f7aeb 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -98,7 +98,7 @@
 
 // Toggles fullscreen mode and waits for the notification.
 void ToggleFullscreenModeAndWait(Browser* browser) {
-  FullscreenNotificationObserver waiter(browser);
+  FullscreenNotificationObserver waiter;
   chrome::ToggleFullscreenMode(browser);
   waiter.Wait();
 }
@@ -106,7 +106,7 @@
 // Enters fullscreen mode for tab and waits for the notification.
 void EnterFullscreenModeForTabAndWait(Browser* browser,
                                       content::WebContents* web_contents) {
-  FullscreenNotificationObserver waiter(browser);
+  FullscreenNotificationObserver waiter;
   browser->exclusive_access_manager()
       ->fullscreen_controller()
       ->EnterFullscreenModeForTab(web_contents, GURL());
@@ -116,13 +116,20 @@
 // Exits fullscreen mode for tab and waits for the notification.
 void ExitFullscreenModeForTabAndWait(Browser* browser,
                                      content::WebContents* web_contents) {
-  FullscreenNotificationObserver waiter(browser);
+  FullscreenNotificationObserver waiter;
   browser->exclusive_access_manager()
       ->fullscreen_controller()
       ->ExitFullscreenModeForTab(web_contents);
   waiter.Wait();
 }
 
+// Exits fullscreen mode and waits for the notification.
+void ExitFullscreenModeAndWait(BrowserView* browser_view) {
+  FullscreenNotificationObserver waiter;
+  browser_view->ExitFullscreen();
+  waiter.Wait();
+}
+
 void StartOverview() {
   ash::Shell::Get()->overview_controller()->StartOverview();
 }
@@ -483,11 +490,7 @@
 
   // Exiting immersive fullscreen should make the caption buttons and the frame
   // visible again.
-  {
-    FullscreenNotificationObserver waiter(browser());
-    browser_view->ExitFullscreen();
-    waiter.Wait();
-  }
+  ExitFullscreenModeAndWait(browser_view);
   EXPECT_FALSE(immersive_mode_controller->IsEnabled());
   EXPECT_TRUE(frame_view->ShouldPaint());
   EXPECT_LT(0, frame_view->GetBoundsForTabStripRegion(browser_view->tabstrip())
diff --git a/chrome/browser/ui/views/frame/hosted_app_ash_interactive_ui_test.cc b/chrome/browser/ui/views/frame/hosted_app_ash_interactive_ui_test.cc
index e973aa1..6d7d723 100644
--- a/chrome/browser/ui/views/frame/hosted_app_ash_interactive_ui_test.cc
+++ b/chrome/browser/ui/views/frame/hosted_app_ash_interactive_ui_test.cc
@@ -5,7 +5,6 @@
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/hosted_app_button_container.h"
@@ -72,7 +71,7 @@
 // Test that the hosted app menu button opens a menu on click in immersive mode.
 IN_PROC_BROWSER_TEST_F(HostedAppAshInteractiveUITest,
                        ImmersiveMenuButtonClickable) {
-  FullscreenNotificationObserver waiter(browser());
+  FullscreenNotificationObserver waiter;
   chrome::ToggleFullscreenMode(browser());
   waiter.Wait();
 
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index 30a104a..8abc833e 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -7,12 +7,15 @@
 #include "ash/public/cpp/immersive/immersive_revealed_lock.h"
 #include "ash/public/cpp/window_properties.h"
 #include "base/macros.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
+#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
@@ -80,10 +83,12 @@
   if (controller_.IsEnabled() == enabled)
     return;
 
-  if (!fullscreen_observer_.IsObservingSources()) {
-    fullscreen_observer_.Add(browser_view_->browser()
-                                 ->exclusive_access_manager()
-                                 ->fullscreen_controller());
+  if (registrar_.IsEmpty()) {
+    content::Source<FullscreenController> source(
+        browser_view_->browser()
+            ->exclusive_access_manager()
+            ->fullscreen_controller());
+    registrar_.Add(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED, source);
   }
 
   ash::ImmersiveFullscreenController::EnableForWidget(browser_view_->frame(),
@@ -216,14 +221,16 @@
   return bounds_in_screen;
 }
 
-void ImmersiveModeControllerAsh::OnFullscreenStateChanged() {
+void ImmersiveModeControllerAsh::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type);
   if (!controller_.IsEnabled())
     return;
 
   // Auto hide the shelf in immersive browser fullscreen.
-  bool in_tab_fullscreen = browser_view_->browser()
-                               ->exclusive_access_manager()
-                               ->fullscreen_controller()
+  bool in_tab_fullscreen = content::Source<FullscreenController>(source)
                                ->IsWindowFullscreenForTabOrPending();
   browser_view_->GetNativeWindow()->SetProperty(
       ash::kHideShelfWhenFullscreenKey, in_tab_fullscreen);
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
index a49aaebc..64d41f5b 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
@@ -11,9 +11,9 @@
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_observer.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
 #include "ui/aura/window_observer.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -24,7 +24,7 @@
 class ImmersiveModeControllerAsh
     : public ImmersiveModeController,
       public ash::ImmersiveFullscreenControllerDelegate,
-      public FullscreenObserver,
+      public content::NotificationObserver,
       public aura::WindowObserver {
  public:
   ImmersiveModeControllerAsh();
@@ -59,10 +59,12 @@
   void SetVisibleFraction(double visible_fraction) override;
   std::vector<gfx::Rect> GetVisibleBoundsInScreen() const override;
 
-  // FullscreenObserver:
-  void OnFullscreenStateChanged() override;
+  // content::NotificationObserver override:
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override;
 
-  // aura::WindowObserver:
+  // aura::WindowObserver override:
   void OnWindowPropertyChanged(aura::Window* window,
                                const void* key,
                                intptr_t old) override;
@@ -80,8 +82,7 @@
   // the top-of-window views are not revealed.
   double visible_fraction_ = 1.0;
 
-  ScopedObserver<FullscreenController, FullscreenObserver> fullscreen_observer_{
-      this};
+  content::NotificationRegistrar registrar_;
 
   ScopedObserver<aura::Window, aura::WindowObserver> observed_windows_{this};
 
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
index c6769c6..d9253a8 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
@@ -25,8 +24,11 @@
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/web_application_info.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/service_manager_connection.h"
 #include "content/public/test/content_mock_cert_verifier.h"
 #include "net/cert/mock_cert_verifier.h"
+#include "services/service_manager/public/cpp/connector.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/views/animation/test/ink_drop_host_view_test_api.h"
 #include "ui/views/window/frame_caption_button.h"
@@ -95,12 +97,12 @@
 
   // Toggle the browser's fullscreen state.
   void ToggleFullscreen() {
-    // The fullscreen change notification is sent asynchronously. The
-    // notification is used to trigger changes in whether the shelf is auto
-    // hidden.
-    FullscreenNotificationObserver waiter(browser());
+    // NOTIFICATION_FULLSCREEN_CHANGED is sent asynchronously. The notification
+    // is used to trigger changes in whether the shelf is auto hidden.
+    std::unique_ptr<FullscreenNotificationObserver> waiter(
+        new FullscreenNotificationObserver());
     chrome::ToggleFullscreenMode(browser());
-    waiter.Wait();
+    waiter->Wait();
   }
 
   // Attempt revealing the top-of-window views.
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
index 21e8cb01..a13169f 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
@@ -54,20 +54,22 @@
 
   // Toggle the browser's fullscreen state.
   void ToggleFullscreen() {
-    // The fullscreen change notification is sent asynchronously. The
-    // notification is used to trigger changes in whether the shelf is auto
-    // hidden and whether a "light bar" version of the tab strip is used when
-    // the top-of-window views are hidden.
-    FullscreenNotificationObserver waiter(browser());
+    // NOTIFICATION_FULLSCREEN_CHANGED is sent asynchronously. The notification
+    // is used to trigger changes in whether the shelf is auto hidden and
+    // whether a "light bar" version of the tab strip is used when the
+    // top-of-window views are hidden.
+    std::unique_ptr<FullscreenNotificationObserver> waiter(
+        new FullscreenNotificationObserver());
     chrome::ToggleFullscreenMode(browser());
-    waiter.Wait();
+    waiter->Wait();
   }
 
   // Set whether the browser is in tab fullscreen.
   void SetTabFullscreen(bool tab_fullscreen) {
     content::WebContents* web_contents =
         browser_view()->contents_web_view()->GetWebContents();
-    FullscreenNotificationObserver waiter(browser());
+    std::unique_ptr<FullscreenNotificationObserver> waiter(
+        new FullscreenNotificationObserver());
     if (tab_fullscreen) {
       browser()
           ->exclusive_access_manager()
@@ -79,7 +81,7 @@
           ->fullscreen_controller()
           ->ExitFullscreenModeForTab(web_contents);
     }
-    waiter.Wait();
+    waiter->Wait();
   }
 
   // Attempt revealing the top-of-window views.
diff --git a/chrome/browser/ui/views/fullscreen_control/fullscreen_control_view_interactive_uitest.cc b/chrome/browser/ui/views/fullscreen_control/fullscreen_control_view_interactive_uitest.cc
index fc9ce93..c83929c 100644
--- a/chrome/browser/ui/views/fullscreen_control/fullscreen_control_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/fullscreen_control/fullscreen_control_view_interactive_uitest.cc
@@ -12,15 +12,16 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_bubble_type.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_host.h"
 #include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_view.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_test_utils.h"
@@ -43,6 +44,19 @@
 
 constexpr base::TimeDelta kPopupEventTimeout = base::TimeDelta::FromSeconds(5);
 
+// Observer for NOTIFICATION_FULLSCREEN_CHANGED notifications.
+class FullscreenNotificationObserver
+    : public content::WindowedNotificationObserver {
+ public:
+  FullscreenNotificationObserver()
+      : WindowedNotificationObserver(
+            chrome::NOTIFICATION_FULLSCREEN_CHANGED,
+            content::NotificationService::AllSources()) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FullscreenNotificationObserver);
+};
+
 }  // namespace
 
 class FullscreenControlViewTest : public InProcessBrowserTest {
@@ -106,7 +120,7 @@
   bool IsPopupCreated() { return GetFullscreenControlHost()->IsPopupCreated(); }
 
   void EnterActiveTabFullscreen() {
-    FullscreenNotificationObserver fullscreen_observer(browser());
+    FullscreenNotificationObserver fullscreen_observer;
     content::WebContentsDelegate* delegate =
         static_cast<content::WebContentsDelegate*>(browser());
     delegate->EnterFullscreenModeForTab(GetActiveWebContents(),
diff --git a/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.cc b/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.cc
index 25296cc..46ac658 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.cc
@@ -5,10 +5,13 @@
 #include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
 
 #include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/grit/generated_resources.h"
+#include "content/public/browser/notification_source.h"
 #include "content/public/browser/render_view_host.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/geometry/rect.h"
@@ -49,8 +52,10 @@
   // Add observer to close the bubble if the fullscreen state changes.
   if (web_contents) {
     Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
-    fullscreen_observer_.Add(
-        browser->exclusive_access_manager()->fullscreen_controller());
+    registrar_.Add(
+        this, chrome::NOTIFICATION_FULLSCREEN_CHANGED,
+        content::Source<FullscreenController>(
+            browser->exclusive_access_manager()->fullscreen_controller()));
   }
   if (!anchor_view)
     SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
@@ -81,7 +86,11 @@
   }
 }
 
-void LocationBarBubbleDelegateView::OnFullscreenStateChanged() {
+void LocationBarBubbleDelegateView::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type);
   GetWidget()->SetVisibilityAnimationTransition(views::Widget::ANIMATE_NONE);
   CloseBubble();
 }
diff --git a/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h b/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h
index 0ed9906..1211914 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h
@@ -6,15 +6,16 @@
 #define CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_LOCATION_BAR_BUBBLE_DELEGATE_VIEW_H_
 
 #include "base/macros.h"
-#include "base/scoped_observer.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_observer.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/events/event_observer.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/event_monitor.h"
 
 namespace content {
+class NotificationDetails;
+class NotificationSource;
 class WebContents;
 }  // namespace content
 
@@ -23,7 +24,7 @@
 // mode.
 // TODO(https://crbug.com/788051): Move to chrome/browser/ui/views/page_action/.
 class LocationBarBubbleDelegateView : public views::BubbleDialogDelegateView,
-                                      public FullscreenObserver,
+                                      public content::NotificationObserver,
                                       public content::WebContentsObserver {
  public:
   enum DisplayReason {
@@ -55,8 +56,10 @@
   // user).
   void ShowForReason(DisplayReason reason, bool allow_refocus_alert = true);
 
-  // FullscreenObserver:
-  void OnFullscreenStateChanged() override;
+  // content::NotificationObserver:
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override;
 
   // content::WebContentsObserver:
   void OnVisibilityChanged(content::Visibility visibility) override;
@@ -96,8 +99,8 @@
   virtual void CloseBubble();
 
  private:
-  ScopedObserver<FullscreenController, FullscreenObserver> fullscreen_observer_{
-      this};
+  // Used to register for fullscreen change notifications.
+  content::NotificationRegistrar registrar_;
 
   DISALLOW_COPY_AND_ASSIGN(LocationBarBubbleDelegateView);
 };
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
index 3096043..96ca287 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
@@ -62,14 +62,15 @@
   // Entering fullscreen should close the bubble. (We enter into tab fullscreen
   // here because tab fullscreen is non-immersive even on Chrome OS.)
   {
-    // The fullscreen change notification is sent asynchronously. Wait for the
+    // NOTIFICATION_FULLSCREEN_CHANGED is sent asynchronously. Wait for the
     // notification before testing the zoom bubble visibility.
-    FullscreenNotificationObserver waiter(browser());
+    std::unique_ptr<FullscreenNotificationObserver> waiter(
+        new FullscreenNotificationObserver());
     browser()
         ->exclusive_access_manager()
         ->fullscreen_controller()
         ->EnterFullscreenModeForTab(web_contents, GURL());
-    waiter.Wait();
+    waiter->Wait();
   }
   ASSERT_FALSE(browser_view->immersive_mode_controller()->IsEnabled());
   EXPECT_FALSE(ZoomBubbleView::GetZoomBubble());
@@ -84,9 +85,10 @@
 
   // Exit fullscreen before ending the test for the sake of sanity.
   {
-    FullscreenNotificationObserver waiter(browser());
+    std::unique_ptr<FullscreenNotificationObserver> waiter(
+        new FullscreenNotificationObserver());
     chrome::ToggleFullscreenMode(browser());
-    waiter.Wait();
+    waiter->Wait();
   }
 }
 
@@ -106,9 +108,10 @@
 
   // Enter immersive fullscreen.
   {
-    FullscreenNotificationObserver waiter(browser());
+    std::unique_ptr<FullscreenNotificationObserver> waiter(
+        new FullscreenNotificationObserver());
     chrome::ToggleFullscreenMode(browser());
-    waiter.Wait();
+    waiter->Wait();
   }
   ASSERT_TRUE(immersive_controller->IsEnabled());
   ASSERT_FALSE(immersive_controller->IsRevealed());
@@ -148,9 +151,10 @@
 
   // Exit fullscreen before ending the test for the sake of sanity.
   {
-    FullscreenNotificationObserver waiter(browser());
+    std::unique_ptr<FullscreenNotificationObserver> waiter(
+        new FullscreenNotificationObserver());
     chrome::ToggleFullscreenMode(browser());
-    waiter.Wait();
+    waiter->Wait();
   }
 }
 #endif  // OS_CHROMEOS
diff --git a/chrome/test/data/chromeos/oobe_webui_browsertest.js b/chrome/test/data/chromeos/oobe_webui_browsertest.js
index aa05af0..de09a39 100644
--- a/chrome/test/data/chromeos/oobe_webui_browsertest.js
+++ b/chrome/test/data/chromeos/oobe_webui_browsertest.js
@@ -34,9 +34,9 @@
   /** @override */
   testGenPreamble: function() {
     // OobeWebUI should run in fullscreen.
-    GEN('FullscreenNotificationObserver fullscreen_observer(browser());');
-    GEN('chrome::ToggleFullscreenMode(browser());');
-    GEN('fullscreen_observer.Wait();');
+    GEN('  FullscreenNotificationObserver fullscreen_observer;');
+    GEN('  chrome::ToggleFullscreenMode(browser());');
+    GEN('  fullscreen_observer.Wait();');
   },
 
   /** @override */