 Chromium Code Reviews
 Chromium Code Reviews Issue 2850403002:
  Fix crash under ExclusiveAccessBubbleViews::AnimationProgressed().  (Closed)
    
  
    Issue 2850403002:
  Fix crash under ExclusiveAccessBubbleViews::AnimationProgressed().  (Closed) 
  | Index: chrome/browser/ui/views/exclusive_access_bubble_views_interactive_uitest.cc | 
| diff --git a/chrome/browser/ui/views/exclusive_access_bubble_views_interactive_uitest.cc b/chrome/browser/ui/views/exclusive_access_bubble_views_interactive_uitest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..729f1b2ecb97ac3ea1bb820f072cc4341e2c4fd6 | 
| --- /dev/null | 
| +++ b/chrome/browser/ui/views/exclusive_access_bubble_views_interactive_uitest.cc | 
| @@ -0,0 +1,78 @@ | 
| +// Copyright 2017 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/fullscreen_controller_test.h" | 
| +#include "chrome/browser/ui/views/exclusive_access_bubble_views.h" | 
| +#include "chrome/browser/ui/views/frame/browser_view.h" | 
| +#include "ui/views/widget/widget.h" | 
| +#include "ui/views/widget/widget_observer.h" | 
| + | 
| +class ExclusiveAccessBubbleViewsTest : public FullscreenControllerTest, | 
| + public views::WidgetObserver { | 
| + public: | 
| + ExclusiveAccessBubbleViewsTest() {} | 
| + | 
| + ExclusiveAccessBubbleViews* bubble() { | 
| + BrowserView* browser_view = | 
| + BrowserView::GetBrowserViewForBrowser(browser()); | 
| + return browser_view->exclusive_access_bubble(); | 
| + } | 
| + | 
| + // WidgetObserver: | 
| + void OnWidgetDestroying(views::Widget* widget) override { | 
| + was_observing_in_destroying_ = widget->HasObserver(bubble()); | 
| + was_destroying_ = true; | 
| + } | 
| + void OnWidgetDestroyed(views::Widget* widget) override { | 
| + widget->RemoveObserver(this); | 
| 
msw
2017/05/03 18:48:33
optional nit: move this into OnWidgetDestroying?
 
tapted
2017/05/04 06:42:08
Done.
 | 
| + } | 
| + | 
| + protected: | 
| + bool was_destroying_ = false; | 
| + bool was_observing_in_destroying_ = false; | 
| + | 
| + private: | 
| + DISALLOW_COPY_AND_ASSIGN(ExclusiveAccessBubbleViewsTest); | 
| +}; | 
| + | 
| +// Simulate obscure codepaths resulting in the bubble Widget being Closed before | 
| 
msw
2017/05/03 18:48:34
nit: "closed"?
 
tapted
2017/05/04 06:42:08
Done.
 | 
| +// the ExclusiveAccessBubbleViews destructor will tear down the bubble and any | 
| 
msw
2017/05/03 18:48:34
nit: this sentence is a bit run-on and could be cl
 
tapted
2017/05/04 06:42:07
Done.
 | 
| +// running animations that could trigger crashes. | 
| +IN_PROC_BROWSER_TEST_F(ExclusiveAccessBubbleViewsTest, NativeClose) { | 
| 
msw
2017/05/03 18:48:34
nit: maybe make a test for the normal close codepa
 
tapted
2017/05/04 06:42:07
Yup - most of the tests in FullscreenControllerInt
 | 
| + EXPECT_FALSE(bubble()); | 
| + EnterActiveTabFullscreen(); | 
| + EXPECT_TRUE(bubble()); | 
| + | 
| + bubble()->GetView()->GetWidget()->AddObserver(this); | 
| + | 
| + // It's hard to nail down a codepath that results in the bubble Widget being | 
| 
msw
2017/05/03 18:48:34
Maybe this comment should just say "Simulate the b
 
tapted
2017/05/04 06:42:07
Done. Yeah is probably too much info.
 | 
| + // destroyed before the call to the ExclusiveAccessBubbleViewsTest destructor. | 
| 
msw
2017/05/03 18:48:34
q: Did you mean ExclusiveAccessBubbleViewsTest? (I
 
tapted
2017/05/04 06:42:07
Ahh yep - I meant the ExclusiveAccessBubbleViews d
 | 
| + // CloseAllTabs() will destroy the bubble via the ExclusiveAccessBubbleViews | 
| + // destructor (via RenderViewTerminated -> ExitFullscreenMode). | 
| + // CloseAllBrowsers() does a graceful exit via Browser::OnWindowClosing() | 
| + // (which triggers CloseAllTabs()). | 
| + // However, when closing via BrowserWindow::DestroyBrowser(), BrowserView | 
| + // first stops observing its Widget, so never sees an OnWindowClosing(). | 
| + // DestroyBrowser is triggered by chrome::SessionEnding() which ends up in | 
| + // RenderProcessHostImpl::FastShutdownIfPossible() and only if fast shutdown | 
| + // IS possible, does it call ProcessDied() which also results in graceful | 
| + // termination via RenderViewTerminated. | 
| + // If fast shutdown is NOT possible (e.g. unload handlers), then the browser | 
| + // window can be torn down before RenderViewTerminated is sent and since the | 
| + // bubble is a child window it will get town down as well. | 
| + // That's all a bit complicated for a test, but it boils down to the window | 
| + // being closed by the OS or by DesktopWindowTreeHost::OnHostClosed() rather | 
| + // than by Widget::Close, and that needs to be handled. Widget::CloseNow() is | 
| + // a close approximation of this. | 
| + bubble()->GetView()->GetWidget()->CloseNow(); | 
| 
msw
2017/05/03 18:48:34
aside: you could also potentially delete the bubbl
 
tapted
2017/05/04 06:42:07
That works for NativeWidgetAura, and views::Platfo
 | 
| + EXPECT_FALSE(bubble()); | 
| + | 
| + // Verify that teardown is really happening via OnWidgetDestroyed() rather | 
| + // than the usual path via the ExclusiveAccessBubbleViews destructor. Since | 
| + // the destructor always first removes ExclusiveAccessBubbleViews as an | 
| + // observer before starting the close, checking in OnWidgetDestroyed that it's | 
| + // still observing achieves this. | 
| + EXPECT_TRUE(was_observing_in_destroying_); | 
| + EXPECT_TRUE(was_destroying_); | 
| +} |